added Malcolm
This commit is contained in:
		
							
								
								
									
										5
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| *.keystore | ||||
| sensor_ctl/*beat/data | ||||
| sensor_ctl/*beat/logs | ||||
| sensor_ctl/supervisor.d/*-*.conf | ||||
| sensor_ctl/extractor_override.zeek | ||||
							
								
								
									
										12
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/init.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										12
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/init.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| SCRIPT_PATH="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" | ||||
| pushd "$SCRIPT_PATH" >/dev/null 2>&1 | ||||
|  | ||||
| if [ -e ./pyenv/bin/activate ]; then | ||||
|   source ./pyenv/bin/activate | ||||
| fi | ||||
|  | ||||
| gunicorn --bind=127.0.0.1:5000 sensor_interface.routes:app | ||||
|  | ||||
| popd >/dev/null 2>&1 | ||||
							
								
								
									
										10
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/kiosk.service
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/kiosk.service
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| [Unit] | ||||
| Description=Hedgehog Linux Kiosk | ||||
|  | ||||
| [Service] | ||||
| WorkingDirectory=/home/sensor/sensor_interface/ | ||||
| ExecStart=/home/sensor/sensor_interface/init.sh | ||||
| Restart=always | ||||
|  | ||||
| [Install] | ||||
| WantedBy=multi-user.target | ||||
| @@ -0,0 +1,16 @@ | ||||
| certifi==2020.6.20 | ||||
| chardet==3.0.4 | ||||
| click==7.1.2 | ||||
| Flask==1.1.2 | ||||
| Flask-Cors==3.0.9 | ||||
| gunicorn==20.0.4 | ||||
| idna==2.10 | ||||
| itsdangerous==1.1.0 | ||||
| Jinja2==2.11.3 | ||||
| MarkupSafe==1.1.1 | ||||
| psutil | ||||
| python-dotenv==0.14.0 | ||||
| requests==2.24.0 | ||||
| six==1.15.0 | ||||
| urllib3==1.26.5 | ||||
| Werkzeug==1.0.1 | ||||
| @@ -0,0 +1,324 @@ | ||||
| #==========================  Modules configuration ============================= | ||||
| auditbeat.modules: | ||||
|  | ||||
| - module: auditd | ||||
|   socket_type: multicast | ||||
|   resolve_ids: true | ||||
|   failure_mode: log | ||||
|   backlog_limit: 16384 | ||||
|   rate_limit: 0 | ||||
|   include_raw_message: false | ||||
|   include_warnings: false | ||||
|   backpressure_strategy: auto | ||||
|   # audit_rule_files: [ '${path.config}/audit.rules.d/*.conf' ] | ||||
|   # no rules specified, auditd will run and manage rules | ||||
|   # see https://www.elastic.co/guide/en/beats/auditbeat/master/auditbeat-module-auditd.html | ||||
|  | ||||
|   # don't forward some things that are always going to be happening | ||||
|   # (/proc/ accesses by beats and browser for the kiosk) to cut down on noise | ||||
|   # and some other approved common stuff that would clutter the logs | ||||
|   processors: | ||||
|   - drop_event: | ||||
|       when: | ||||
|         or: | ||||
|           - and: | ||||
|               - equals: | ||||
|                   auditd.data.syscall: 'setsockopt' | ||||
|               - equals: | ||||
|                   auditd.summary.object.type: 'network-device' | ||||
|               - or: | ||||
|                   - equals: | ||||
|                       auditd.summary.how: '/usr/sbin/tcpdump' | ||||
|                   - equals: | ||||
|                       auditd.summary.how: '/opt/zeek/bin/zeek' | ||||
|                   - equals: | ||||
|                       auditd.summary.how: '/usr/sbin/netsniff-ng' | ||||
|                   - equals: | ||||
|                       auditd.summary.how: '/opt/moloch/bin/moloch-capture' | ||||
|           - and: | ||||
|               - equals: | ||||
|                   auditd.message_type: 'syscall' | ||||
|               - equals: | ||||
|                   auditd.summary.object.type: 'file' | ||||
|               - or: | ||||
|                 - and: | ||||
|                     - or: | ||||
|                         - equals: | ||||
|                             auditd.data.syscall: 'open' | ||||
|                         - equals: | ||||
|                             auditd.data.syscall: 'openat' | ||||
|                     - regexp: | ||||
|                         auditd.summary.object.primary: '^/(proc/|etc/localtime|usr/lib/x86_64-linux-gnu/gconv/gconv-modules\.cache)' | ||||
|                     - or: | ||||
|                         - equals: | ||||
|                             auditd.summary.how: '/usr/share/auditbeat/bin/auditbeat' | ||||
|                         - equals: | ||||
|                             auditd.summary.how: '/usr/share/metricbeat/bin/metricbeat' | ||||
|                         - equals: | ||||
|                             auditd.summary.how: '/opt/firefox/firefox-bin' | ||||
|                         - equals: | ||||
|                             auditd.summary.how: '/opt/firefox/firefox' | ||||
|                         - equals: | ||||
|                             auditd.summary.how: '/usr/sbin/tcpdump' | ||||
|                         - equals: | ||||
|                             auditd.summary.how: '/opt/zeek/bin/zeek' | ||||
|                         - equals: | ||||
|                             auditd.summary.how: '/usr/sbin/netsniff-ng' | ||||
|                         - equals: | ||||
|                             auditd.summary.how: '/opt/moloch/bin/moloch-capture' | ||||
|                 - and: | ||||
|                     - or: | ||||
|                         - equals: | ||||
|                             auditd.data.syscall: 'open' | ||||
|                         - equals: | ||||
|                             auditd.data.syscall: 'openat' | ||||
|                     - not: | ||||
|                         has_fields: ['auditd.summary.object.primary'] | ||||
|                     - or: | ||||
|                       - equals: | ||||
|                           auditd.summary.how: 'supervisorctl' | ||||
|                       - equals: | ||||
|                           auditd.summary.how: '/usr/share/metricbeat/bin/metricbeat' | ||||
|                 - and: | ||||
|                     - equals: | ||||
|                         auditd.data.syscall: 'open' | ||||
|                     - regexp: | ||||
|                         auditd.summary.object.primary: '^/dev/' | ||||
|                     - equals: | ||||
|                         auditd.summary.how: '/usr/sbin/hddtemp' | ||||
|                 - and: | ||||
|                     - equals: | ||||
|                         auditd.data.syscall: 'open' | ||||
|                     - regexp: | ||||
|                         auditd.summary.object.primary: '^/.+/__pycache__/$' | ||||
|                     - regexp: | ||||
|                         auditd.summary.how: '^python3' | ||||
|                 - and: | ||||
|                     - or: | ||||
|                       - equals: | ||||
|                           auditd.summary.how: 'bash' | ||||
|                       - equals: | ||||
|                           auditd.summary.how: '/bin/bash' | ||||
|                     - equals: | ||||
|                         auditd.summary.actor.primary: 'sensor' | ||||
|                     - equals: | ||||
|                         auditd.data.syscall: 'chown' | ||||
|                     - equals: | ||||
|                         auditd.summary.object.primary: '/home/sensor/.bash_history' | ||||
|                 - and: | ||||
|                     - equals: | ||||
|                         auditd.summary.how: '/opt/firefox/firefox-bin' | ||||
|                     - equals: | ||||
|                         auditd.summary.actor.primary: 'sensor' | ||||
|                     - or: | ||||
|                         - equals: | ||||
|                             auditd.data.syscall: 'chmod' | ||||
|                         - equals: | ||||
|                             auditd.data.syscall: 'open' | ||||
|                         - equals: | ||||
|                             auditd.data.syscall: 'openat' | ||||
|                         - equals: | ||||
|                             auditd.data.syscall: 'rename' | ||||
|                         - equals: | ||||
|                             auditd.data.syscall: 'renameat' | ||||
|                         - equals: | ||||
|                             auditd.data.syscall: 'rmdir' | ||||
|                         - equals: | ||||
|                             auditd.data.syscall: 'unlink' | ||||
|                     - or: | ||||
|                         - regexp: | ||||
|                             auditd.summary.object.primary: '^/home/sensor/\.(cache/)?mozilla/firefox/' | ||||
|                         - regexp: | ||||
|                             auditd.summary.object.primary: '^/home/sensor/\.config/mimeapps\.' | ||||
|                         - regexp: | ||||
|                             auditd.summary.object.primary: '^/tmp/(Temp-|firefox)' | ||||
|                         - equals: | ||||
|                             auditd.summary.object.primary: '/opt/firefox/fonts/' | ||||
|                 - and: | ||||
|                     - or: | ||||
|                         - equals: | ||||
|                             auditd.data.syscall: 'open' | ||||
|                         - equals: | ||||
|                             auditd.data.syscall: 'openat' | ||||
|                         - equals: | ||||
|                             auditd.data.syscall: 'unlink' | ||||
|                         - equals: | ||||
|                             auditd.data.syscall: 'rename' | ||||
|                         - equals: | ||||
|                             auditd.data.syscall: 'renameat' | ||||
|                     - regexp: | ||||
|                         auditd.summary.object.primary: '^/(capture/|usr/lib/python)' | ||||
|                     - or: | ||||
|                         - equals: | ||||
|                             auditd.summary.how: 'zeekctl' | ||||
|                         - equals: | ||||
|                             auditd.summary.how: 'supervisorctl' | ||||
|                 - and: | ||||
|                     - equals: | ||||
|                         auditd.summary.object.primary: '/opt/sensor/sensor_ctl/filebeat/data/registry/filebeat/data.json' | ||||
|                     - equals: | ||||
|                         auditd.summary.how: '/usr/share/filebeat/bin/filebeat' | ||||
|                 # I don't like filtering this, but see https://github.com/zeek/zeek/issues/259#issuecomment-564735946 | ||||
|                 # zeekctl is doing this, but there's nothing to indicate that's what it is. | ||||
|                 # For now I've tried to narrow it as much as possible, because it's *so* much noise | ||||
|                 - and: | ||||
|                     - or: | ||||
|                         - equals: | ||||
|                             auditd.data.syscall: 'unlink' | ||||
|                         - equals: | ||||
|                             auditd.data.syscall: 'openat' | ||||
|                     - equals: | ||||
|                         auditd.result: 'fail' | ||||
|                     - equals: | ||||
|                         auditd.summary.how: 'python' | ||||
|                     - regexp: | ||||
|                         auditd.summary.object.primary: '^/usr/lib/python' | ||||
|  | ||||
|  | ||||
| - module: file_integrity | ||||
|   paths: | ||||
|   - /bin | ||||
|   - /opt/zeek | ||||
|   - /sbin | ||||
|   - /usr/bin | ||||
|   - /usr/local/bin | ||||
|   - /usr/sbin | ||||
|  | ||||
| # system module is experimental: https://www.elastic.co/blog/introducing-auditbeat-system-module | ||||
| # ALSO system module is ONLY available with non-OSS license | ||||
|  | ||||
| # - module: system | ||||
| #   datasets: | ||||
| #     - host # General host information, e.g. uptime, IPs | ||||
| #     - user # User information | ||||
| #   period: 1m | ||||
| #   user.detect_password_changes: true | ||||
|  | ||||
| # - module: system | ||||
| #   datasets: | ||||
| #     - process # Started and stopped processes | ||||
| #     - socket  # Opened and closed sockets | ||||
| #   period: 1s | ||||
|  | ||||
| #   # drop noise | ||||
| #   processors: | ||||
| #   - drop_event: | ||||
| #       when: | ||||
| #         or: | ||||
| #           - and: | ||||
| #               - equals: | ||||
| #                   event.module: 'system' | ||||
| #               - equals: | ||||
| #                   event.dataset: 'socket' | ||||
| #               - equals: | ||||
| #                   destination.ip: '127.0.0.1' | ||||
| #               - equals: | ||||
| #                   source.ip: '127.0.0.1' | ||||
| #           - and: | ||||
| #               - equals: | ||||
| #                   event.module: 'system' | ||||
| #               - equals: | ||||
| #                   event.dataset: 'socket' | ||||
| #               - equals: | ||||
| #                   destination.ip: "${BEAT_ES_HOST}" | ||||
| #               - equals: | ||||
| #                   destination.port: "${BEAT_ES_PORT}" | ||||
| #           - and: | ||||
| #               - equals: | ||||
| #                   event.module: 'system' | ||||
| #               - equals: | ||||
| #                   event.dataset: 'socket' | ||||
| #               - equals: | ||||
| #                   destination.ip: "${BEAT_KIBANA_HOST}" | ||||
| #               - equals: | ||||
| #                   destination.port: "${BEAT_KIBANA_PORT}" | ||||
| #           - and: | ||||
| #               - equals: | ||||
| #                   event.module: 'system' | ||||
| #               - equals: | ||||
| #                   event.dataset: 'process' | ||||
| #               - or: | ||||
| #                   - equals: | ||||
| #                       process.executable: '/bin/sleep' | ||||
| #                   - equals: | ||||
| #                       process.executable: '/usr/bin/sort' | ||||
| #                   - equals: | ||||
| #                       process.executable: '/usr/bin/tail' | ||||
| #                   - equals: | ||||
| #                       process.executable: '/usr/bin/clear' | ||||
| #                   - equals: | ||||
| #                       process.executable: '/usr/bin/head' | ||||
| #                   - equals: | ||||
| #                       process.executable: '/bin/date' | ||||
| #                   - equals: | ||||
| #                       process.executable: '/bin/ls' | ||||
| #                   - equals: | ||||
| #                       process.executable: '/usr/bin/stat' | ||||
| #                   - equals: | ||||
| #                       process.executable: '/usr/bin/cut' | ||||
| #                   - equals: | ||||
| #                       process.executable: '/usr/bin/xargs' | ||||
| #                   - equals: | ||||
| #                       process.executable: '/usr/bin/tr' | ||||
| #                   - equals: | ||||
| #                       process.executable: '/bin/grep' | ||||
| #                   - equals: | ||||
| #                       process.executable: '/bin/sed' | ||||
| #                   - equals: | ||||
| #                       process.executable: '/bin/df' | ||||
| #                   - equals: | ||||
| #                       process.executable: '/usr/bin/du' | ||||
| #                   - equals: | ||||
| #                       process.executable: '/usr/bin/gawk' | ||||
| #                   - and: | ||||
| #                       - equals: | ||||
| #                           process.executable: '/bin/bash' | ||||
| #                       - contains: | ||||
| #                           process.args: '/usr/local/bin/prune_files.sh' | ||||
| #                   - and: | ||||
| #                       - equals: | ||||
| #                           process.executable: '/usr/bin/find' | ||||
| #                       - contains: | ||||
| #                           process.args: '-xdev' | ||||
| #                       - contains: | ||||
| #                           process.args: '-mindepth' | ||||
| #                       - contains: | ||||
| #                           process.args: '-maxdepth' | ||||
| #                       - contains: | ||||
| #                           process.args: '-printf' | ||||
|  | ||||
| #================================ General ====================================== | ||||
| fields_under_root: true | ||||
|  | ||||
| #================================ Outputs ====================================== | ||||
|  | ||||
| #-------------------------- Elasticsearch output ------------------------------- | ||||
| output.elasticsearch: | ||||
|   enabled: true | ||||
|   hosts: ["${BEAT_ES_HOST}:${BEAT_ES_PORT}"] | ||||
|   protocol: "${BEAT_ES_PROTOCOL}" | ||||
|   username: "${BEAT_HTTP_USERNAME}" | ||||
|   password: "${BEAT_HTTP_PASSWORD}" | ||||
|   ssl.verification_mode: "${BEAT_ES_SSL_VERIFY}" | ||||
|  | ||||
| setup.template.enabled: true | ||||
| setup.template.overwrite: false | ||||
| setup.template.settings: | ||||
|   index.number_of_shards: 1 | ||||
|   index.number_of_replicas: 0 | ||||
|  | ||||
| #============================== Dashboards ===================================== | ||||
| setup.dashboards.enabled: "${BEAT_KIBANA_DASHBOARDS_ENABLED}" | ||||
| setup.dashboards.directory: "${BEAT_KIBANA_DASHBOARDS_PATH}" | ||||
|  | ||||
| #============================== Kibana ===================================== | ||||
| setup.kibana: | ||||
|   host: "${BEAT_KIBANA_HOST}:${BEAT_KIBANA_PORT}" | ||||
|   protocol: "${BEAT_KIBANA_PROTOCOL}" | ||||
|   username: "${BEAT_HTTP_USERNAME}" | ||||
|   password: "${BEAT_HTTP_PASSWORD}" | ||||
|   ssl.verification_mode: "${BEAT_KIBANA_SSL_VERIFY}" | ||||
|  | ||||
| #================================ Logging ====================================== | ||||
| logging.metrics.enabled: false | ||||
|  | ||||
| @@ -0,0 +1,19 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # Copyright (c) 2021 Battelle Energy Alliance, LLC.  All rights reserved. | ||||
|  | ||||
| # force-navigate to script directory (containing config file) | ||||
| [[ "$(uname -s)" = 'Darwin' ]] && REALPATH=grealpath || REALPATH=realpath | ||||
| [[ "$(uname -s)" = 'Darwin' ]] && DIRNAME=gdirname || DIRNAME=dirname | ||||
| if ! (type "$REALPATH" && type "$DIRNAME") > /dev/null; then | ||||
|   echo "$(basename "${BASH_SOURCE[0]}") requires $REALPATH and $DIRNAME" | ||||
|   exit 1 | ||||
| fi | ||||
| SCRIPT_PATH="$($DIRNAME $($REALPATH -e "${BASH_SOURCE[0]}"))" | ||||
| pushd "$SCRIPT_PATH" >/dev/null 2>&1 | ||||
|  | ||||
| mkdir -p "$SCRIPT_PATH/data" | ||||
|  | ||||
| auditbeat --path.home "$SCRIPT_PATH" --path.config "$SCRIPT_PATH" --path.data "$SCRIPT_PATH/data" -c "$SCRIPT_PATH/auditbeat.yml" -e | ||||
|  | ||||
| popd  >/dev/null 2>&1 | ||||
							
								
								
									
										1
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_ctl/clean
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_ctl/clean
									
									
									
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| clean.sh | ||||
							
								
								
									
										22
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_ctl/clean.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										22
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_ctl/clean.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # Copyright (c) 2021 Battelle Energy Alliance, LLC.  All rights reserved. | ||||
|  | ||||
| set -e | ||||
|  | ||||
| SCRIPT_PATH="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" | ||||
| pushd "$SCRIPT_PATH" >/dev/null 2>&1 | ||||
|  | ||||
| CONTROL_VARS_FILE="control_vars.conf" | ||||
| source "$CONTROL_VARS_FILE" | ||||
|  | ||||
| if [ -n "$PCAP_PATH" ] && [ "$PCAP_PATH" != "/" ] && [ -d "$PCAP_PATH" ] ; then | ||||
|   PCAP_SIZE="$(du -sh "$PCAP_PATH"/ | cut -f1)" | ||||
|   rm -rf "$PCAP_PATH"/* && echo "Removed $PCAP_SIZE from packet capture path" | ||||
| fi | ||||
| if [ -n "$ZEEK_LOG_PATH" ] && [ "$ZEEK_LOG_PATH" != "/" ] && [ -d "$ZEEK_LOG_PATH" ] ; then | ||||
|   ZEEK_SIZE="$(du -sh "$ZEEK_LOG_PATH"/ | cut -f1)" | ||||
|   rm -rf "$ZEEK_LOG_PATH"/* && echo "Removed $ZEEK_SIZE from Zeek log path" | ||||
| fi | ||||
|  | ||||
| popd >/dev/null 2>&1 | ||||
							
								
								
									
										72
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_ctl/control.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										72
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_ctl/control.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # Copyright (c) 2021 Battelle Energy Alliance, LLC.  All rights reserved. | ||||
|  | ||||
| set -e | ||||
|  | ||||
| CONFIG_DIR="supervisor.d" | ||||
| CONFIG_FILE="supervisord.conf" | ||||
| CONTROL_VARS_FILE="control_vars.conf" | ||||
|  | ||||
| # create symlinks to this script named start, stop, restart, etc. | ||||
| CONTROL_COMMAND="$(basename "${BASH_SOURCE[0]}")" | ||||
|  | ||||
| if [[ ($# -eq 0) || (($# -eq 1) && ("$1" = "all")) ]]; then | ||||
|   # no arguments, defaults to all managed processes | ||||
|   CONTROL_PROCESS="all" | ||||
| else | ||||
|   # eg., tcpdump, zeek | ||||
|   CONTROL_PROCESS="$@" | ||||
| fi | ||||
|  | ||||
| # force-navigate to script directory (containing config file) | ||||
| [[ "$(uname -s)" = 'Darwin' ]] && REALPATH=grealpath || REALPATH=realpath | ||||
| [[ "$(uname -s)" = 'Darwin' ]] && DIRNAME=gdirname || DIRNAME=dirname | ||||
| if ! (type "$REALPATH" && type "$DIRNAME") > /dev/null; then | ||||
|   echo "$(basename "${BASH_SOURCE[0]}") requires $REALPATH and $DIRNAME" | ||||
|   exit 1 | ||||
| fi | ||||
| # SUPERVISOR_PATH is exported to be referenced in supervisord.conf | ||||
| export SUPERVISOR_PATH="$($DIRNAME $($REALPATH -e "${BASH_SOURCE[0]}"))" | ||||
| pushd "$SUPERVISOR_PATH" >/dev/null 2>&1 | ||||
|  | ||||
| source "$CONTROL_VARS_FILE" | ||||
|  | ||||
| if [[ -d ./"$CONFIG_DIR" && ("$CONTROL_COMMAND" = "start" || "$CONTROL_COMMAND" = "restart") && "$CONTROL_PROCESS" = "all" ]]; then | ||||
|   # if it's simply a bare "start" or "restart", we only really want it to restart the programs for which | ||||
|   # autostart=true, instead of ALL possible programs. So look in $CONFIG_FILE for autostart=true processes | ||||
|   # and execute the $CONTROL_COMMAND only on those programs | ||||
|   CONTROL_PROGS=() | ||||
|   while read LINE; do | ||||
|     PROCESS_NAME=$(echo $LINE | cut -f1 -d=) | ||||
|     PROCESS_AUTOSTART_VAR=$(echo $LINE | cut -f2 -d=) | ||||
|     PROCESS_AUTOSTART="$(eval echo "\$$PROCESS_AUTOSTART_VAR")" | ||||
|     if [ "${PROCESS_AUTOSTART,,}" = "true" ]; then | ||||
|       # | ||||
|       CONTROL_PROGS+=("$PROCESS_NAME") | ||||
|     fi | ||||
|   done <<< "$(grep -Pih "^(\[program:|autostart\s*=)" ./"$CONFIG_DIR"/*.conf | sed -e "s/\[program:\(.*\)\]/\1/" | sed -e "s/.*ENV_\(.*\))s/\1/" | paste -s -d'=\n')" | ||||
|  | ||||
|   # because we're using group names now, we need to use the full group name in our start/restart command rather than just the command name | ||||
|   # get it from the "status" command | ||||
|   CONTROL_PROGS_WITH_GROUP=() | ||||
|   while read LINE; do | ||||
|     for PROG in "${CONTROL_PROGS[@]}"; do | ||||
|       if [[ $LINE =~ ^[a-zA-Z0-9_-]+:${PROG}$ ]]; then | ||||
|         CONTROL_PROGS_WITH_GROUP+=("$LINE") | ||||
|         break | ||||
|       fi | ||||
|     done | ||||
|   done <<< "$(supervisorctl -c "$CONFIG_FILE" status all | awk '{print $1}')" | ||||
|  | ||||
|   # issue the command with the full, group-included names | ||||
|   if [ ${#CONTROL_PROGS_WITH_GROUP[@]} -gt 0 ]; then | ||||
|     supervisorctl -c "$CONFIG_FILE" "$CONTROL_COMMAND" "${CONTROL_PROGS_WITH_GROUP[@]}" | ||||
|   fi | ||||
|  | ||||
| else | ||||
|   # simply pass the command through to supervisorctl | ||||
|   supervisorctl -c "$CONFIG_FILE" "$CONTROL_COMMAND" "${CONTROL_PROCESS[@]}" | ||||
| fi | ||||
|  | ||||
| popd >/dev/null 2>&1 | ||||
| @@ -0,0 +1,93 @@ | ||||
| export CAPTURE_INTERFACE=eth0 | ||||
| export CAPTURE_FILTER="" | ||||
|  | ||||
| export PCAP_PATH=/home/sensor/net_cap | ||||
| export PCAP_TCPDUMP_FILENAME_PATTERN=%Y%m%d_%H%M%S.pcap | ||||
| export PCAP_NETSNIFF_MAGIC=0xa1b2c3d4 | ||||
| export PCAP_ROTATE_SECONDS=10800 | ||||
| export PCAP_ROTATE_MEGABYTES=500 | ||||
| export PCAP_SNAPLEN=0 | ||||
| export PCAP_MAX_DISK_FILL=90 | ||||
| export PCAP_PRUNE_CHECK_SECONDS=60 | ||||
|  | ||||
| export ARKIME_VIEWER_PORT=8005 | ||||
| export ARKIME_PACKET_THREADS=5 | ||||
| export ARKIME_PACKET_ACL= | ||||
|  | ||||
| export PROTOLOGBEAT_PORT=9515 | ||||
| export PROTOLOGBEAT_INTERVAL=10 | ||||
|  | ||||
| export ZEEK_LOG_PATH=/home/sensor/bro_logs | ||||
| export ZEEK_MAX_DISK_FILL=90 | ||||
| export ZEEK_PRUNE_CHECK_SECONDS=90 | ||||
|  | ||||
| # Zeek performance tuning (node.cfg, see idaholab/Malcolm#36 for details) | ||||
| export ZEEK_PIN_CPUS_LOGGER= | ||||
| export ZEEK_PIN_CPUS_MANAGER= | ||||
| export ZEEK_PIN_CPUS_PROXY= | ||||
| # zeekdeploy.sh will also use (if present, where n is the number of capture interfaces): | ||||
| # - ZEEK_PIN_CPUS_WORKER_1 .. ZEEK_PIN_CPUS_WORKER_n | ||||
| # - ZEEK_LB_PROCS_WORKER_1 .. ZEEK_LB_PROCS_WORKER_n (falling back to ZEEK_LB_PROCS) | ||||
| export ZEEK_LB_PROCS=1 | ||||
| export ZEEK_LB_METHOD=custom | ||||
| export ZEEK_AF_PACKET_BUFFER_SIZE=67108864 | ||||
|  | ||||
| export ZEEK_RULESET=local | ||||
| export ZEEK_EXTRACTOR_MODE=none | ||||
| export ZEEK_EXTRACTOR_OVERRIDE_FILE= | ||||
| export EXTRACTED_FILE_MIN_BYTES=64 | ||||
| export EXTRACTED_FILE_MAX_BYTES=134217728 | ||||
| export EXTRACTED_FILE_PRESERVATION=quarantined | ||||
| export ZEEK_DISABLE_HASH_ALL_FILES= | ||||
| export ZEEK_DISABLE_LOG_PASSWORDS= | ||||
| export ZEEK_DISABLE_SSL_VALIDATE_CERTS= | ||||
| export ZEEK_DISABLE_TRACK_ALL_ASSETS= | ||||
| export ZEEK_DISABLE_BEST_GUESS_ICS=true | ||||
| export ZEEK_DISABLE_SPICY_DHCP=true | ||||
| export ZEEK_DISABLE_SPICY_DNS=true | ||||
| export ZEEK_DISABLE_SPICY_HTTP=true | ||||
| export ZEEK_DISABLE_SPICY_IPSEC= | ||||
| export ZEEK_DISABLE_SPICY_OPENVPN= | ||||
| export ZEEK_DISABLE_SPICY_TFTP= | ||||
| export ZEEK_DISABLE_SPICY_WIREGUARD= | ||||
|  | ||||
| # affects Arkime only for now: beats values are stored in keystores per-beat | ||||
| export ES_PROTOCOL=https | ||||
| export ES_HOST=127.0.0.1 | ||||
| export ES_PORT=9200 | ||||
| export ES_USERNAME=sensor | ||||
| export ES_PASSWORD=%70%61%73%73%77%6F%72%64 | ||||
| export ES_SSL_VERIFY=none | ||||
|  | ||||
| export VTOT_REQUESTS_PER_MINUTE=4 | ||||
| export VTOT_API2_KEY="" | ||||
| export MALASS_HOST="" | ||||
| export MALASS_PORT=80 | ||||
| export MALASS_MAX_REQUESTS=20 | ||||
| export CLAMD_MAX_REQUESTS=8 | ||||
| export EXTRACTED_FILE_YARA_CUSTOM_ONLY=false | ||||
| export YARA_MAX_REQUESTS=8 | ||||
| export YARA_RULES_DIR=/opt/yara-rules | ||||
| export CAPA_VERBOSE=false | ||||
| export CAPA_MAX_REQUESTS=4 | ||||
|  | ||||
| export ZEEK_FILE_WATCH=false | ||||
| export ZEEK_FILE_SCAN_CLAMAV=false | ||||
| export ZEEK_FILE_SCAN_VTOT=false | ||||
| export ZEEK_FILE_SCAN_MALASS=false | ||||
| export ZEEK_FILE_SCAN_YARA=false | ||||
| export ZEEK_FILE_SCAN_CAPA=false | ||||
|  | ||||
| export AUTOSTART_AUDITBEAT=false | ||||
| export AUTOSTART_CLAMAV_UPDATES=false | ||||
| export AUTOSTART_FILEBEAT=false | ||||
| export AUTOSTART_HEATBEAT=false | ||||
| export AUTOSTART_HEATBEAT_SENSORS=false | ||||
| export AUTOSTART_METRICBEAT=false | ||||
| export AUTOSTART_ARKIME=false | ||||
| export AUTOSTART_NETSNIFF=false | ||||
| export AUTOSTART_PRUNE_PCAP=false | ||||
| export AUTOSTART_PRUNE_ZEEK=false | ||||
| export AUTOSTART_SYSLOGBEAT=false | ||||
| export AUTOSTART_TCPDUMP=false | ||||
| export AUTOSTART_ZEEK=false | ||||
| @@ -0,0 +1,106 @@ | ||||
| #!/usr/bin/env zeek | ||||
|  | ||||
| # Copyright (c) 2021 Battelle Energy Alliance, LLC.  All rights reserved. | ||||
|  | ||||
| export { | ||||
|   redef extractor_always_extract_unknown = F; | ||||
|  | ||||
|   redef extractor_mime_to_ext_map : table[string] of string = { | ||||
|     ["application/binary"]= "bin", | ||||
|     ["application/ecmascript"]= "es", | ||||
|     ["application/hta"]= "hta", | ||||
|     ["application/java-archive"]= "jar", | ||||
|     ["application/java-serialized-object"]= "ser", | ||||
|     ["application/java-vm"]= "class", | ||||
|     ["application/javascript"]= "js", | ||||
|     ["application/ms-vsi"]= "vsi", | ||||
|     ["application/msaccess"]= "accdb", | ||||
|     ["application/msaccess.addin"]= "accda", | ||||
|     ["application/msaccess.cab"]= "accdc", | ||||
|     ["application/msaccess.ftemplate"]= "accft", | ||||
|     ["application/msaccess.runtime"]= "accdr", | ||||
|     ["application/msaccess.webapplication"]= "accdw", | ||||
|     ["application/msexcel"]= "xls", | ||||
|     ["application/mspowerpoint"]= "ppt", | ||||
|     ["application/msword"]= "doc", | ||||
|     ["application/octet-stream"]= "bin", | ||||
|     ["application/pdf"]= "pdf", | ||||
|     ["application/PowerShell"]= "psc1", | ||||
|     ["application/rtf"]= "rtf", | ||||
|     ["application/vnd.apple.installer+xml"]= "mpkg", | ||||
|     ["application/vnd.microsoft.portable-executable"]= "exe", | ||||
|     ["application/vnd.ms-cab-compressed"]= "cab", | ||||
|     ["application/vnd.ms-excel"]= "xls", | ||||
|     ["application/vnd.ms-excel.addin.macroEnabled.12"]= "xlam", | ||||
|     ["application/vnd.ms-excel.addin.macroenabled.12"]= "xlam", | ||||
|     ["application/vnd.ms-excel.sheet.binary.macroEnabled.12"]= "xlsb", | ||||
|     ["application/vnd.ms-excel.sheet.binary.macroenabled.12"]= "xlsb", | ||||
|     ["application/vnd.ms-excel.sheet.macroEnabled.12"]= "xlsm", | ||||
|     ["application/vnd.ms-excel.sheet.macroenabled.12"]= "xlsm", | ||||
|     ["application/vnd.ms-excel.template.macroEnabled.12"]= "xltm", | ||||
|     ["application/vnd.ms-excel.template.macroenabled.12"]= "xltm", | ||||
|     ["application/vnd.ms-office.calx"]= "calx", | ||||
|     ["application/vnd.ms-officetheme"]= "thmx", | ||||
|     ["application/vnd.ms-powerpoint"]= "ppt", | ||||
|     ["application/vnd.ms-powerpoint.addin.macroEnabled.12"]= "ppam", | ||||
|     ["application/vnd.ms-powerpoint.addin.macroenabled.12"]= "ppam", | ||||
|     ["application/vnd.ms-powerpoint.presentation.macroEnabled.12"]= "pptm", | ||||
|     ["application/vnd.ms-powerpoint.presentation.macroenabled.12"]= "pptm", | ||||
|     ["application/vnd.ms-powerpoint.slide.macroEnabled.12"]= "sldm", | ||||
|     ["application/vnd.ms-powerpoint.slide.macroenabled.12"]= "sldm", | ||||
|     ["application/vnd.ms-powerpoint.slideshow.macroEnabled.12"]= "ppsm", | ||||
|     ["application/vnd.ms-powerpoint.slideshow.macroenabled.12"]= "ppsm", | ||||
|     ["application/vnd.ms-powerpoint.template.macroEnabled.12"]= "potm", | ||||
|     ["application/vnd.ms-powerpoint.template.macroenabled.12"]= "potm", | ||||
|     ["application/vnd.ms-word.document.macroEnabled.12"]= "docm", | ||||
|     ["application/vnd.ms-word.document.macroenabled.12"]= "docm", | ||||
|     ["application/vnd.ms-word.template.macroEnabled.12"]= "dotm", | ||||
|     ["application/vnd.ms-word.template.macroenabled.12"]= "dotm", | ||||
|     ["application/vnd.openofficeorg.extension"]= "oxt", | ||||
|     ["application/vnd.openxmlformats-officedocument.presentationml.presentation"]= "pptx", | ||||
|     ["application/vnd.openxmlformats-officedocument.presentationml.slide"]= "sldx", | ||||
|     ["application/vnd.openxmlformats-officedocument.presentationml.slideshow"]= "ppsx", | ||||
|     ["application/vnd.openxmlformats-officedocument.presentationml.template"]= "potx", | ||||
|     ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]= "xlsx", | ||||
|     ["application/vnd.openxmlformats-officedocument.spreadsheetml.template"]= "xltx", | ||||
|     ["application/vnd.openxmlformats-officedocument.wordprocessingml.document"]= "docx", | ||||
|     ["application/vnd.openxmlformats-officedocument.wordprocessingml.template"]= "dotx", | ||||
|     ["application/windows-library+xml"]= "library-ms", | ||||
|     ["application/x-7z-compressed"]= "7z", | ||||
|     ["application/x-ace-compressed"]= "ace", | ||||
|     ["application/x-apple-diskimage"]= "dmg", | ||||
|     ["application/x-bzip"]= "bz", | ||||
|     ["application/x-bzip2"]= "bz2", | ||||
|     ["application/x-cfs-compressed"]= "cfs", | ||||
|     ["application/x-compress"]= "z", | ||||
|     ["application/x-compressed"]= "tgz", | ||||
|     ["application/x-cpio"]= "cpio", | ||||
|     ["application/x-csh"]= "csh", | ||||
|     ["application/x-dgc-compressed"]= "dgc", | ||||
|     ["application/x-dosexec"]= "exe", | ||||
|     ["application/x-elf"]= "elf", | ||||
|     ["application/x-executable"]= "exe", | ||||
|     ["application/x-gca-compressed"]= "gca", | ||||
|     ["application/x-gtar"]= "gtar", | ||||
|     ["application/x-gzip"]= "gz", | ||||
|     ["application/x-install-instructions"]= "install", | ||||
|     ["application/x-lzh-compressed"]= "lzh", | ||||
|     ["application/x-ms-application"]= "application", | ||||
|     ["application/x-ms-installer"]= "msi", | ||||
|     ["application/x-ms-shortcut"]= "lnk", | ||||
|     ["application/x-msdos-program"]= "exe", | ||||
|     ["application/x-msdownload"]= "exe", | ||||
|     ["application/x-pe-app-32bit-i386"]= "exe", | ||||
|     ["application/x-perl"]= "pl", | ||||
|     ["application/x-python"]= "py", | ||||
|     ["application/x-rar-compressed"]= "rar", | ||||
|     ["application/x-sh"]= "sh", | ||||
|     ["application/x-shockwave-flash"]= "swf", | ||||
|     ["application/x-zip-compressed"]= "zip", | ||||
|     ["application/zip"]= "zip", | ||||
|     ["text/jscript"]= "jsx", | ||||
|     ["text/rtf"]= "rtf", | ||||
|     ["text/vbscript"]= "vbs" | ||||
|   } &default="dat"; | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,39 @@ | ||||
| filebeat.inputs: | ||||
| - type: syslog | ||||
|   protocol.udp: | ||||
|     host: "127.0.0.1:9514" | ||||
|  | ||||
| #================================ General ====================================== | ||||
| fields_under_root: true | ||||
|  | ||||
| #================================ Outputs ====================================== | ||||
|  | ||||
| #-------------------------- Elasticsearch output ------------------------------- | ||||
| output.elasticsearch: | ||||
|   enabled: true | ||||
|   hosts: ["${BEAT_ES_HOST}:${BEAT_ES_PORT}"] | ||||
|   protocol: "${BEAT_ES_PROTOCOL}" | ||||
|   username: "${BEAT_HTTP_USERNAME}" | ||||
|   password: "${BEAT_HTTP_PASSWORD}" | ||||
|   ssl.verification_mode: "${BEAT_ES_SSL_VERIFY}" | ||||
|  | ||||
| setup.template.enabled: true | ||||
| setup.template.overwrite: false | ||||
| setup.template.settings: | ||||
|   index.number_of_shards: 1 | ||||
|   index.number_of_replicas: 0 | ||||
|  | ||||
| #============================== Dashboards ===================================== | ||||
| setup.dashboards.enabled: "${BEAT_KIBANA_DASHBOARDS_ENABLED}" | ||||
| setup.dashboards.directory: "${BEAT_KIBANA_DASHBOARDS_PATH}" | ||||
|  | ||||
| #============================== Kibana ===================================== | ||||
| setup.kibana: | ||||
|   host: "${BEAT_KIBANA_HOST}:${BEAT_KIBANA_PORT}" | ||||
|   protocol: "${BEAT_KIBANA_PROTOCOL}" | ||||
|   username: "${BEAT_HTTP_USERNAME}" | ||||
|   password: "${BEAT_HTTP_PASSWORD}" | ||||
|   ssl.verification_mode: "${BEAT_KIBANA_SSL_VERIFY}" | ||||
|  | ||||
| #================================ Logging ====================================== | ||||
| logging.metrics.enabled: false | ||||
| @@ -0,0 +1,19 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # Copyright (c) 2021 Battelle Energy Alliance, LLC.  All rights reserved. | ||||
|  | ||||
| # force-navigate to script directory (containing config file) | ||||
| [[ "$(uname -s)" = 'Darwin' ]] && REALPATH=grealpath || REALPATH=realpath | ||||
| [[ "$(uname -s)" = 'Darwin' ]] && DIRNAME=gdirname || DIRNAME=dirname | ||||
| if ! (type "$REALPATH" && type "$DIRNAME") > /dev/null; then | ||||
|   echo "$(basename "${BASH_SOURCE[0]}") requires $REALPATH and $DIRNAME" | ||||
|   exit 1 | ||||
| fi | ||||
| SCRIPT_PATH="$($DIRNAME $($REALPATH -e "${BASH_SOURCE[0]}"))" | ||||
| pushd "$SCRIPT_PATH" >/dev/null 2>&1 | ||||
|  | ||||
| mkdir -p "$SCRIPT_PATH/data" | ||||
|  | ||||
| filebeat --path.home "$SCRIPT_PATH" --path.config "$SCRIPT_PATH" --path.data "$SCRIPT_PATH/data" -c "$SCRIPT_PATH/filebeat-syslog.yml" --modules=system -e | ||||
|  | ||||
| popd  >/dev/null 2>&1 | ||||
| @@ -0,0 +1,34 @@ | ||||
| # Copyright (c) 2021 Battelle Energy Alliance, LLC.  All rights reserved. | ||||
|  | ||||
| logging.metrics.enabled: false | ||||
|  | ||||
| filebeat.inputs: | ||||
| - type: log | ||||
|   paths: | ||||
|     - ${BEAT_LOG_PATTERN:/home/sensor/bro_logs/*.log} | ||||
|   symlinks: true | ||||
|   fields_under_root: true | ||||
|   # tags: ["remote"] | ||||
|   fields: | ||||
|     type: "session" | ||||
|   compression_level: 0 | ||||
|   exclude_lines: ['^\s*#'] | ||||
|   scan_frequency: ${BEAT_SCAN_FREQUENCY:10s} | ||||
|   clean_inactive: ${BEAT_CLEAN_INACTIVE:180m} | ||||
|   ignore_older: ${BEAT_IGNORE_OLDER:120m} | ||||
|   close_inactive: ${BEAT_CLOSE_INACTIVE:90m} | ||||
|   close_renamed: ${BEAT_CLOSE_RENAMED:true} | ||||
|   close_removed: ${BEAT_CLOSE_REMOVED:true} | ||||
|   close_eof: ${BEAT_CLOSE_EOF:false} | ||||
|   clean_renamed: ${BEAT_CLEAN_RENAMED:true} | ||||
|   clean_removed: ${BEAT_CLEAN_REMOVED:true} | ||||
|  | ||||
| output.logstash: | ||||
|   hosts: ["${BEAT_LS_HOST}:${BEAT_LS_PORT}"] | ||||
|   ssl.enabled: ${BEAT_LS_SSL:false} | ||||
|   ssl.certificate_authorities: ["${BEAT_LS_SSL_CA_CRT}"] | ||||
|   ssl.certificate: "${BEAT_LS_SSL_CLIENT_CRT}" | ||||
|   ssl.key: "${BEAT_LS_SSL_CLIENT_KEY}" | ||||
|   ssl.supported_protocols: "TLSv1.2" | ||||
|   ssl.verification_mode: "${BEAT_LS_SSL_VERIFY}" | ||||
|  | ||||
| @@ -0,0 +1,43 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # Copyright (c) 2021 Battelle Energy Alliance, LLC.  All rights reserved. | ||||
|  | ||||
| if [ -z "$CAPTURE_PATH" ]; then | ||||
|   CAPTURE_PATH="$HOME/bro_logs" | ||||
| fi | ||||
| export CAPTURE_PATH | ||||
| export BEAT_LOG_PATTERN="${CAPTURE_PATH}/*.log" | ||||
| export BEAT_SCAN_FREQUENCY="10s" | ||||
| export BEAT_CLEAN_INACTIVE="180m" | ||||
| export BEAT_IGNORE_OLDER="120m" | ||||
| export BEAT_CLOSE_INACTIVE="90m" | ||||
| export BEAT_CLOSE_RENAMED="true" | ||||
| export BEAT_CLOSE_REMOVED="true" | ||||
| export BEAT_CLOSE_EOF="false" | ||||
| export BEAT_CLEAN_RENAMED="true" | ||||
| export BEAT_CLEAN_REMOVED="true" | ||||
|  | ||||
| SLEEP_SEC=0 | ||||
| while getopts s: opts; do | ||||
|    case ${opts} in | ||||
|       s) SLEEP_SEC=${OPTARG} ;; | ||||
|    esac | ||||
| done | ||||
|  | ||||
| # force-navigate to script directory (containing config file) | ||||
| [[ "$(uname -s)" = 'Darwin' ]] && REALPATH=grealpath || REALPATH=realpath | ||||
| [[ "$(uname -s)" = 'Darwin' ]] && DIRNAME=gdirname || DIRNAME=dirname | ||||
| if ! (type "$REALPATH" && type "$DIRNAME") > /dev/null; then | ||||
|   echo "$(basename "${BASH_SOURCE[0]}") requires $REALPATH and $DIRNAME" | ||||
|   exit 1 | ||||
| fi | ||||
| SCRIPT_PATH="$($DIRNAME $($REALPATH -e "${BASH_SOURCE[0]}"))" | ||||
| pushd "$SCRIPT_PATH" >/dev/null 2>&1 | ||||
|  | ||||
| mkdir -p "$SCRIPT_PATH/data" | ||||
|  | ||||
| sleep $SLEEP_SEC | ||||
|  | ||||
| filebeat --path.home "$SCRIPT_PATH" --path.config "$SCRIPT_PATH" --path.data "$SCRIPT_PATH/data" -c "$SCRIPT_PATH/filebeat.yml" -e | ||||
|  | ||||
| popd  >/dev/null 2>&1 | ||||
| @@ -0,0 +1,117 @@ | ||||
| { | ||||
|   "index_patterns": ["protologbeat-*"], | ||||
|   "mappings": { | ||||
|     "_meta": { | ||||
|       "version": "7.6.2" | ||||
|     }, | ||||
|     "dynamic_templates": [{ | ||||
|       "strings_as_keyword": { | ||||
|         "mapping": { | ||||
|           "ignore_above": 1024, | ||||
|           "type": "keyword" | ||||
|         }, | ||||
|         "match_mapping_type": "string" | ||||
|       } | ||||
|     }], | ||||
|     "properties": { | ||||
|       "@timestamp": { | ||||
|         "type": "date" | ||||
|       }, | ||||
|       "cpu_rpm_avg": { | ||||
|         "type": "float" | ||||
|       }, | ||||
|       "cpu_volt_avg": { | ||||
|         "type": "float" | ||||
|       }, | ||||
|       "cpu_temp_avg": { | ||||
|         "type": "float" | ||||
|       }, | ||||
|       "gpu_rpm_avg": { | ||||
|         "type": "float" | ||||
|       }, | ||||
|       "gpu_volt_avg": { | ||||
|         "type": "float" | ||||
|       }, | ||||
|       "gpu_temp_avg": { | ||||
|         "type": "float" | ||||
|       }, | ||||
|       "hdd_temp_avg": { | ||||
|         "type": "float" | ||||
|       }, | ||||
|       "other_volt_avg": { | ||||
|         "type": "float" | ||||
|       }, | ||||
|       "other_rpm_avg": { | ||||
|         "type": "float" | ||||
|       }, | ||||
|       "other_temp_avg": { | ||||
|         "type": "float" | ||||
|       }, | ||||
|       "sensors": { | ||||
|         "properties": { | ||||
|           "adapter": { | ||||
|             "ignore_above": 1024, | ||||
|             "type": "keyword", | ||||
|             "norms": false | ||||
|           }, | ||||
|           "class": { | ||||
|             "ignore_above": 1024, | ||||
|             "type": "keyword", | ||||
|             "norms": false | ||||
|           }, | ||||
|           "label": { | ||||
|             "ignore_above": 1024, | ||||
|             "type": "keyword", | ||||
|             "norms": false | ||||
|           }, | ||||
|           "name": { | ||||
|             "ignore_above": 1024, | ||||
|             "type": "keyword", | ||||
|             "norms": false | ||||
|           }, | ||||
|           "units": { | ||||
|             "ignore_above": 1024, | ||||
|             "type": "keyword", | ||||
|             "norms": false | ||||
|           }, | ||||
|           "value": { | ||||
|             "type": "float" | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "beat": { | ||||
|         "properties": { | ||||
|           "hostname": { | ||||
|             "ignore_above": 1024, | ||||
|             "type": "keyword" | ||||
|           }, | ||||
|           "name": { | ||||
|             "ignore_above": 1024, | ||||
|             "type": "keyword" | ||||
|           }, | ||||
|           "version": { | ||||
|             "ignore_above": 1024, | ||||
|             "type": "keyword" | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "host": { | ||||
|         "properties": { | ||||
|           "name": { | ||||
|             "ignore_above": 1024, | ||||
|             "type": "keyword" | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "tags": { | ||||
|         "ignore_above": 1024, | ||||
|         "type": "keyword" | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   "order": 0, | ||||
|   "settings": { | ||||
|     "index.mapping.total_fields.limit": 5000, | ||||
|     "index.refresh_interval": "5s" | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,49 @@ | ||||
| protologbeat: | ||||
|   address: "127.0.0.1" | ||||
|   port: 9515 | ||||
|   protocol: udp | ||||
|   json_mode: true | ||||
|   merge_fields_to_root: true | ||||
|   max_msg_size: 8192 | ||||
|   default_es_log_type: protologbeat | ||||
|  | ||||
| #================================ General ====================================== | ||||
| fields_under_root: true | ||||
|  | ||||
| #============================== Template ======================================= | ||||
|  | ||||
| setup.template.enabled: true | ||||
| setup.template.overwrite: false | ||||
| setup.template.settings: | ||||
|   index.number_of_shards: 1 | ||||
|   index.number_of_replicas: 0 | ||||
| setup.template.json.enabled: true | ||||
| setup.template.json.path: "${path.config}/protologbeat.template.json" | ||||
| setup.template.json.name: "protologbeat" | ||||
|  | ||||
| #================================ Outputs ====================================== | ||||
|  | ||||
| #-------------------------- Elasticsearch output ------------------------------- | ||||
| output.elasticsearch: | ||||
|   enabled: true | ||||
|   hosts: ["${BEAT_ES_HOST}:${BEAT_ES_PORT}"] | ||||
|   protocol: "${BEAT_ES_PROTOCOL}" | ||||
|   username: "${BEAT_HTTP_USERNAME}" | ||||
|   password: "${BEAT_HTTP_PASSWORD}" | ||||
|   ssl.verification_mode: "${BEAT_ES_SSL_VERIFY}" | ||||
|   template.versions.2x.enabled: false | ||||
|  | ||||
| #============================== Dashboards ===================================== | ||||
| setup.dashboards.enabled: "${BEAT_KIBANA_DASHBOARDS_ENABLED}" | ||||
| setup.dashboards.directory: "${BEAT_KIBANA_DASHBOARDS_PATH}" | ||||
|  | ||||
| #============================== Kibana ===================================== | ||||
| setup.kibana: | ||||
|   host: "${BEAT_KIBANA_HOST}:${BEAT_KIBANA_PORT}" | ||||
|   protocol: "${BEAT_KIBANA_PROTOCOL}" | ||||
|   username: "${BEAT_HTTP_USERNAME}" | ||||
|   password: "${BEAT_HTTP_PASSWORD}" | ||||
|   ssl.verification_mode: "${BEAT_KIBANA_SSL_VERIFY}" | ||||
|  | ||||
| #================================ Logging ====================================== | ||||
| logging.metrics.enabled: false | ||||
| @@ -0,0 +1,19 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # Copyright (c) 2021 Battelle Energy Alliance, LLC.  All rights reserved. | ||||
|  | ||||
| # force-navigate to script directory (containing config file) | ||||
| [[ "$(uname -s)" = 'Darwin' ]] && REALPATH=grealpath || REALPATH=realpath | ||||
| [[ "$(uname -s)" = 'Darwin' ]] && DIRNAME=gdirname || DIRNAME=dirname | ||||
| if ! (type "$REALPATH" && type "$DIRNAME") > /dev/null; then | ||||
|   echo "$(basename "${BASH_SOURCE[0]}") requires $REALPATH and $DIRNAME" | ||||
|   exit 1 | ||||
| fi | ||||
| SCRIPT_PATH="$($DIRNAME $($REALPATH -e "${BASH_SOURCE[0]}"))" | ||||
| pushd "$SCRIPT_PATH" >/dev/null 2>&1 | ||||
|  | ||||
| mkdir -p "$SCRIPT_PATH/data" | ||||
|  | ||||
| protologbeat --path.home "$SCRIPT_PATH" --path.config "$SCRIPT_PATH" --path.data "$SCRIPT_PATH/data" -c "$SCRIPT_PATH/protologbeat.yml" -e | ||||
|  | ||||
| popd  >/dev/null 2>&1 | ||||
| @@ -0,0 +1,75 @@ | ||||
| metricbeat.config.modules: | ||||
|   path: ${path.config}/conf.d/*.yml | ||||
|   reload.period: 10s | ||||
|   reload.enabled: false | ||||
|  | ||||
| metricbeat.max_start_delay: 10s | ||||
|  | ||||
| #==========================  Modules configuration ============================ | ||||
| metricbeat.modules: | ||||
|  | ||||
| #------------------------------- System Module ------------------------------- | ||||
| - module: system | ||||
|   period: ${BEAT_INTERVAL} | ||||
|   metricsets: | ||||
|     - cpu             # CPU usage | ||||
|     - load            # CPU load averages | ||||
|     - memory          # Memory usage | ||||
|     - network         # Network IO | ||||
|     - process         # Per process metrics | ||||
|     - process_summary # Process summary | ||||
|     - uptime          # System Uptime | ||||
|     - diskio          # Disk IO | ||||
|   enabled: true | ||||
|   processes: ['.*'] | ||||
|   process.include_top_n: | ||||
|     enabled: true | ||||
|     by_cpu: 10 | ||||
|     by_memory: 10 | ||||
|  | ||||
|   cpu.metrics:  ["percentages"] | ||||
|   core.metrics: ["percentages"] | ||||
|  | ||||
| - module: system | ||||
|   period: 1m | ||||
|   metricsets: | ||||
|     - filesystem     # File system usage for each mountpoint | ||||
|     - fsstat         # File system summary metrics | ||||
|   processors: | ||||
|   - drop_event.when.regexp: | ||||
|       system.filesystem.mount_point: '^/(sys|cgroup|proc|dev|etc|host|lib|boot)($|/)' | ||||
|  | ||||
| #================================ General ====================================== | ||||
| fields_under_root: true | ||||
|  | ||||
| #================================ Outputs ====================================== | ||||
|  | ||||
| #-------------------------- Elasticsearch output ------------------------------- | ||||
| output.elasticsearch: | ||||
|   enabled: true | ||||
|   hosts: ["${BEAT_ES_HOST}:${BEAT_ES_PORT}"] | ||||
|   protocol: "${BEAT_ES_PROTOCOL}" | ||||
|   username: "${BEAT_HTTP_USERNAME}" | ||||
|   password: "${BEAT_HTTP_PASSWORD}" | ||||
|   ssl.verification_mode: "${BEAT_ES_SSL_VERIFY}" | ||||
|  | ||||
| setup.template.enabled: true | ||||
| setup.template.overwrite: false | ||||
| setup.template.settings: | ||||
|   index.number_of_shards: 1 | ||||
|   index.number_of_replicas: 0 | ||||
|  | ||||
| #============================== Dashboards ===================================== | ||||
| setup.dashboards.enabled: "${BEAT_KIBANA_DASHBOARDS_ENABLED}" | ||||
| setup.dashboards.directory: "${BEAT_KIBANA_DASHBOARDS_PATH}" | ||||
|  | ||||
| #============================== Kibana ===================================== | ||||
| setup.kibana: | ||||
|   host: "${BEAT_KIBANA_HOST}:${BEAT_KIBANA_PORT}" | ||||
|   protocol: "${BEAT_KIBANA_PROTOCOL}" | ||||
|   username: "${BEAT_HTTP_USERNAME}" | ||||
|   password: "${BEAT_HTTP_PASSWORD}" | ||||
|   ssl.verification_mode: "${BEAT_KIBANA_SSL_VERIFY}" | ||||
|  | ||||
| #================================ Logging ====================================== | ||||
| logging.metrics.enabled: false | ||||
| @@ -0,0 +1,19 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # Copyright (c) 2021 Battelle Energy Alliance, LLC.  All rights reserved. | ||||
|  | ||||
| # force-navigate to script directory (containing config file) | ||||
| [[ "$(uname -s)" = 'Darwin' ]] && REALPATH=grealpath || REALPATH=realpath | ||||
| [[ "$(uname -s)" = 'Darwin' ]] && DIRNAME=gdirname || DIRNAME=dirname | ||||
| if ! (type "$REALPATH" && type "$DIRNAME") > /dev/null; then | ||||
|   echo "$(basename "${BASH_SOURCE[0]}") requires $REALPATH and $DIRNAME" | ||||
|   exit 1 | ||||
| fi | ||||
| SCRIPT_PATH="$($DIRNAME $($REALPATH -e "${BASH_SOURCE[0]}"))" | ||||
| pushd "$SCRIPT_PATH" >/dev/null 2>&1 | ||||
|  | ||||
| mkdir -p "$SCRIPT_PATH/data" | ||||
|  | ||||
| metricbeat --path.home "$SCRIPT_PATH" --path.config "$SCRIPT_PATH" --path.data "$SCRIPT_PATH/data" -c "$SCRIPT_PATH/metricbeat.yml" -e | ||||
|  | ||||
| popd  >/dev/null 2>&1 | ||||
| @@ -0,0 +1,60 @@ | ||||
| # these settings for moloch-capture will be overridden on the command-line (with "-o field=value") | ||||
| # so you can (for the most part) ignore settings here that seem like dummy settings | ||||
|  | ||||
| [default] | ||||
| elasticsearch=http://192.168.0.1:9200 | ||||
| rotateIndex=daily | ||||
| passwordSecret=Malcolm | ||||
| httpRealm=Arkime | ||||
| interface=enp0s1 | ||||
| pcapDir=/tmp | ||||
| maxFileSizeG=2 | ||||
| maxFileTimeM=180 | ||||
| tcpTimeout=600 | ||||
| tcpSaveTimeout=720 | ||||
| udpTimeout=30 | ||||
| icmpTimeout=10 | ||||
| maxStreams=1000000 | ||||
| maxPackets=10000 | ||||
| freeSpaceG=5% | ||||
| viewPort=8005 | ||||
| geoLite2Country=/dummy/GeoLite2-Country.mmdb | ||||
| geoLite2ASN=/dummy/GeoLite2-ASN.mmdb | ||||
| rirFile=/dummy/ipv4-address-space.csv | ||||
| ouiFile=/dummy/oui.txt | ||||
| dropUser=sensor | ||||
| dropGroup=netdev | ||||
| parseSMTP=true | ||||
| parseSMB=true | ||||
| parseQSValue=false | ||||
| supportSha256=false | ||||
| maxReqBody=64 | ||||
| config.reqBodyOnlyUtf8=true | ||||
| smtpIpHeaders=X-Originating-IP:;X-Barracuda-Apparent-Source-IP: | ||||
| parsersDir=/dummy/parsers | ||||
| pluginsDir=/dummy/plugins | ||||
| spiDataMaxIndices=2 | ||||
| compressES=false | ||||
| maxESConns=30 | ||||
| maxESRequests=500 | ||||
| packetsPerPoll=50000 | ||||
| antiSynDrop=false | ||||
| logEveryXPackets=500000 | ||||
| logUnknownProtocols=false | ||||
| logESRequests=false | ||||
| logFileCreation=true | ||||
| logHTTPConnections=false | ||||
|  | ||||
| ### High Performance settings | ||||
| # https://github.com/arkime/arkime/wiki/Settings#High_Performance_Settings | ||||
| magicMode=basic | ||||
| pcapReadMethod=tpacketv3 | ||||
| tpacketv3NumThreads=2 | ||||
| tpacketv3BlockSize=8388608 | ||||
| pcapWriteMethod=simple | ||||
| pcapWriteSize=2560000 | ||||
| packetThreads=5 | ||||
| maxPacketsInQueue=300000 | ||||
| dbBulkSize=4000000 | ||||
| #compressES=true | ||||
| rulesFiles=/dummy/rules.yml | ||||
| @@ -0,0 +1,25 @@ | ||||
| --- | ||||
| version: 1 | ||||
| rules: | ||||
|   - name: "Only save first n packets of TLS" | ||||
|     when: "fieldSet" | ||||
|     fields: | ||||
|       protocols: | ||||
|       - tls | ||||
|     ops: | ||||
|       _maxPacketsToSave: 15 | ||||
|   - name: "Only save first n packets of SSH" | ||||
|     when: "fieldSet" | ||||
|     fields: | ||||
|       protocols: | ||||
|       - ssh | ||||
|     ops: | ||||
|       _maxPacketsToSave: 20 | ||||
|   - name: "Dont save SPI sessions with only 1 source packet" | ||||
|     when: "beforeFinalSave" | ||||
|     fields: | ||||
|       packets.src: 1 | ||||
|       packets.dst: 0 | ||||
|       tcpflags.syn: 1 | ||||
|     ops: | ||||
|       _dontSaveSPI: 1 | ||||
							
								
								
									
										1
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_ctl/reload
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_ctl/reload
									
									
									
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| control.sh | ||||
| @@ -0,0 +1 @@ | ||||
| control.sh | ||||
| @@ -0,0 +1,6 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # Copyright (c) 2021 Battelle Energy Alliance, LLC.  All rights reserved. | ||||
|  | ||||
| SPACE_STRING="$(/bin/df -lh --output=source,target,avail,size,pcent | tail -n +2 | grep '^/dev' |  tr -s ' ' ',' | cut -d, -f2,3,4,5 | sed 's/^/\[/' | sed 's/$/\]/' | tr '\n' '.')" | ||||
| logger "${SPACE_STRING}" | ||||
| @@ -0,0 +1,9 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # Copyright (c) 2021 Battelle Energy Alliance, LLC.  All rights reserved. | ||||
|  | ||||
| AVG_TEMP="$(sensors 2>/dev/null | grep '^Core\s[[:digit:]]\+:' | sed -e 's/[[:space:]]\+/,/g' | cut -d',' -f3 | sed "s/^\+//" | sed "s/°.*//" | awk '{ total += $1; count++ } END { if (count > 0) { print total/count } }')" | ||||
| HDD_TEMP="$(hddtemp /dev/sd? 2>/dev/null | grep -v "S\.M\.A\.R\.T\. not available" | sed 's/^/\[/' | sed 's/$/\]/' | tr '\n' ',' | sed 's/,$//')" | ||||
| if [ -n "$AVG_TEMP" ] || [ -n "$HDD_TEMP" ] ; then | ||||
|   logger "CPUs: ${AVG_TEMP}°C, HDDs: ${HDD_TEMP}" | ||||
| fi | ||||
| @@ -0,0 +1 @@ | ||||
| control.sh | ||||
							
								
								
									
										1
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_ctl/start
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_ctl/start
									
									
									
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| control.sh | ||||
							
								
								
									
										1
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_ctl/status
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_ctl/status
									
									
									
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| control.sh | ||||
							
								
								
									
										1
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_ctl/stop
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_ctl/stop
									
									
									
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| control.sh | ||||
| @@ -0,0 +1,56 @@ | ||||
| [group:beats] | ||||
| programs=filebeat,syslogbeat,metricbeat,auditbeat,heatbeat,sensors | ||||
|  | ||||
| [program:filebeat] | ||||
| environment=CAPTURE_PATH=%(ENV_ZEEK_LOG_PATH)s/logs/current | ||||
| command=bash -l %(ENV_SUPERVISOR_PATH)s/filebeat/sensor_filebeat_local.sh -s 20 | ||||
| startsecs=25 | ||||
| startretries=3 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| autostart=%(ENV_AUTOSTART_FILEBEAT)s | ||||
| directory=%(ENV_SUPERVISOR_PATH)s/filebeat | ||||
|  | ||||
| [program:syslogbeat] | ||||
| command=bash -l %(ENV_SUPERVISOR_PATH)s/filebeat-syslog/sensor_filebeat-syslog_local.sh | ||||
| startsecs=5 | ||||
| startretries=3 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| autostart=%(ENV_AUTOSTART_SYSLOGBEAT)s | ||||
| directory=%(ENV_SUPERVISOR_PATH)s/filebeat-syslog | ||||
|  | ||||
| [program:metricbeat] | ||||
| command=bash -l %(ENV_SUPERVISOR_PATH)s/metricbeat/sensor_metricbeat_local.sh | ||||
| startsecs=5 | ||||
| startretries=3 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| autostart=%(ENV_AUTOSTART_METRICBEAT)s | ||||
| directory=%(ENV_SUPERVISOR_PATH)s/metricbeat | ||||
|  | ||||
| [program:auditbeat] | ||||
| command=bash -l %(ENV_SUPERVISOR_PATH)s/auditbeat/sensor_auditbeat_local.sh | ||||
| startsecs=5 | ||||
| startretries=3 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| autostart=%(ENV_AUTOSTART_AUDITBEAT)s | ||||
| directory=%(ENV_SUPERVISOR_PATH)s/auditbeat | ||||
|  | ||||
| [program:heatbeat] | ||||
| command=bash -l %(ENV_SUPERVISOR_PATH)s/heatbeat/sensor_heatbeat_local.sh | ||||
| startsecs=5 | ||||
| startretries=3 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| autostart=%(ENV_AUTOSTART_HEATBEAT)s | ||||
| directory=%(ENV_SUPERVISOR_PATH)s/heatbeat | ||||
|  | ||||
| [program:sensors] | ||||
| command=/usr/bin/python3 /usr/local/bin/beat-log-temperature.py -p %(ENV_PROTOLOGBEAT_PORT)s -c 0 -s %(ENV_PROTOLOGBEAT_INTERVAL)s | ||||
| startsecs=5 | ||||
| startretries=3 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| autostart=%(ENV_AUTOSTART_HEATBEAT_SENSORS)s | ||||
| @@ -0,0 +1,22 @@ | ||||
| [group:clamav] | ||||
| programs=clamav-service,clamav-updates | ||||
|  | ||||
| [program:clamav-updates] | ||||
| command=/usr/bin/freshclam freshclam --user sensor --config-file=/etc/clamav/freshclam.conf --daemon | ||||
| user=sensor | ||||
| autostart=%(ENV_AUTOSTART_CLAMAV_UPDATES)s | ||||
| autorestart=true | ||||
| startsecs=0 | ||||
| startretries=0 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
|  | ||||
| [program:clamav-service] | ||||
| command=/usr/sbin/clamd -c /etc/clamav/clamd.conf | ||||
| user=sensor | ||||
| autostart=%(ENV_ZEEK_FILE_SCAN_CLAMAV)s | ||||
| autorestart=true | ||||
| startsecs=0 | ||||
| startretries=0 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| @@ -0,0 +1,40 @@ | ||||
| [group:moloch] | ||||
| programs=moloch-capture,moloch-viewer | ||||
|  | ||||
| [program:moloch-viewer] | ||||
| command=/opt/moloch/bin/node /opt/moloch/viewer/viewer.js | ||||
|   -c "%(ENV_SUPERVISOR_PATH)s"/moloch/config.ini %(ENV_ARKIME_HTTPS_FLAG)s | ||||
|   -o pcapDir="%(ENV_PCAP_PATH)s" | ||||
|   -o viewPort=%(ENV_ARKIME_VIEWER_PORT)s | ||||
| startsecs=5 | ||||
| startretries=2000000000 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| autostart=%(ENV_AUTOSTART_ARKIME)s | ||||
| directory=%(ENV_SUPERVISOR_PATH)s/moloch | ||||
|  | ||||
| [program:moloch-capture] | ||||
| command=/opt/moloch/bin/moloch-capture %(ENV_ARKIME_HTTPS_FLAG)s | ||||
|   -c "%(ENV_SUPERVISOR_PATH)s"/moloch/config.ini | ||||
|   -o pcapDir="%(ENV_PCAP_PATH)s" | ||||
|   -o bpf="%(ENV_CAPTURE_FILTER)s" | ||||
|   -o packetThreads=%(ENV_ARKIME_PACKET_THREADS)s | ||||
|   -o dropUser=sensor | ||||
|   -o dropGroup=netdev | ||||
|   -o geoLite2Country="%(ENV_SUPERVISOR_PATH)s"/moloch/GeoLite2-Country.mmdb | ||||
|   -o geoLite2ASN="%(ENV_SUPERVISOR_PATH)s"/moloch/GeoLite2-ASN.mmdb | ||||
|   -o rirFile="%(ENV_SUPERVISOR_PATH)s"/moloch/ipv4-address-space.csv | ||||
|   -o ouiFile="%(ENV_SUPERVISOR_PATH)s"/moloch/oui.txt | ||||
|   -o rulesFiles="%(ENV_SUPERVISOR_PATH)s"/moloch/rules.yml | ||||
|   -o parsersDir=/opt/moloch/parsers | ||||
|   -o pluginsDir=/opt/moloch/plugins | ||||
|   --node "%(ENV_ARKIME_NODE_NAME)s" | ||||
|   --host "%(ENV_ARKIME_NODE_HOST)s" | ||||
| startsecs=30 | ||||
| startretries=2000000000 | ||||
| autorestart=true | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| autostart=%(ENV_AUTOSTART_ARKIME)s | ||||
| directory=%(ENV_PCAP_PATH)s | ||||
| user=sensor | ||||
| @@ -0,0 +1,9 @@ | ||||
| [program:netsniff-$IFACE] | ||||
| command=/usr/sbin/netsniff-ng -i "$IFACE" -T "%(ENV_PCAP_NETSNIFF_MAGIC)s" -o "%(ENV_PCAP_PATH)s" -P "netsniff-$IFACE_" -F "%(ENV_PCAP_ROTATE_MEGABYTES)sMiB" --silent "%(ENV_CAPTURE_FILTER)s" | ||||
| startsecs=5 | ||||
| startretries=3 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| autostart=%(ENV_AUTOSTART_NETSNIFF)s | ||||
| directory=%(ENV_PCAP_PATH)s | ||||
| user=sensor | ||||
| @@ -0,0 +1,20 @@ | ||||
| [group:prune] | ||||
| programs=prune-pcap,prune-zeek | ||||
|  | ||||
| [program:prune-pcap] | ||||
| command=bash -l /usr/local/bin/prune_files.sh -p %(ENV_PCAP_PATH)s -t %(ENV_PCAP_MAX_DISK_FILL)s -i %(ENV_PCAP_PRUNE_CHECK_SECONDS)s -r | ||||
| startsecs=5 | ||||
| startretries=3 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| autostart=%(ENV_AUTOSTART_PRUNE_PCAP)s | ||||
| directory=%(ENV_PCAP_PATH)s | ||||
|  | ||||
| [program:prune-zeek] | ||||
| command=bash -l /usr/local/bin/prune_files.sh -p %(ENV_ZEEK_LOG_PATH)s -t %(ENV_ZEEK_MAX_DISK_FILL)s -i %(ENV_ZEEK_PRUNE_CHECK_SECONDS)s -r | ||||
| startsecs=5 | ||||
| startretries=3 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| autostart=%(ENV_AUTOSTART_PRUNE_ZEEK)s | ||||
| directory=%(ENV_ZEEK_LOG_PATH)s | ||||
| @@ -0,0 +1,9 @@ | ||||
| [program:tcpdump-$IFACE] | ||||
| command=/usr/sbin/tcpdump -i "$IFACE" -s %(ENV_PCAP_SNAPLEN)s -w "tcpdump-$IFACE_%(ENV_PCAP_TCPDUMP_FILENAME_PATTERN)s" -G %(ENV_PCAP_ROTATE_SECONDS)s -C %(ENV_PCAP_ROTATE_MEGABYTES)s -K -n "%(ENV_CAPTURE_FILTER)s" | ||||
| startsecs=5 | ||||
| startretries=3 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| autostart=%(ENV_AUTOSTART_TCPDUMP)s | ||||
| directory=%(ENV_PCAP_PATH)s | ||||
| user=sensor | ||||
| @@ -0,0 +1,110 @@ | ||||
| [group:zeek] | ||||
| programs=zeekctl,watcher,virustotal,clamav,yara,capa,malass,logger | ||||
|  | ||||
| [program:zeekctl] | ||||
| command=/opt/zeek/bin/zeekdeploy.sh | ||||
| startsecs=10 | ||||
| stopwaitsecs=15 | ||||
| startretries=3 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| autostart=%(ENV_AUTOSTART_ZEEK)s | ||||
| directory=%(ENV_ZEEK_LOG_PATH)s | ||||
| user=sensor | ||||
|  | ||||
| [program:watcher] | ||||
| command=/usr/bin/python3 /usr/local/bin/zeek_carve_watcher.py | ||||
|   --start-sleep 90 | ||||
|   --min-bytes %(ENV_EXTRACTED_FILE_MIN_BYTES)s | ||||
|   --max-bytes %(ENV_EXTRACTED_FILE_MAX_BYTES)s | ||||
|   --directory "%(ENV_ZEEK_LOG_PATH)s/extract_files" | ||||
| startsecs=100 | ||||
| startretries=3 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| autostart=%(ENV_ZEEK_FILE_WATCH)s | ||||
| directory=%(ENV_ZEEK_LOG_PATH)s | ||||
| user=sensor | ||||
|  | ||||
| [program:virustotal] | ||||
| command=/usr/bin/python3 /usr/local/bin/zeek_carve_scanner.py | ||||
|   --start-sleep 20 | ||||
|   --vtot-api "%(ENV_VTOT_API2_KEY)s" | ||||
|   --req-limit %(ENV_VTOT_REQUESTS_PER_MINUTE)s | ||||
| startsecs=30 | ||||
| startretries=3 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| autostart=%(ENV_ZEEK_FILE_SCAN_VTOT)s | ||||
| directory=%(ENV_ZEEK_LOG_PATH)s | ||||
| user=sensor | ||||
|  | ||||
| [program:clamav] | ||||
| command=/usr/bin/python3 /usr/local/bin/zeek_carve_scanner.py | ||||
|   --start-sleep 20 | ||||
|   --clamav %(ENV_ZEEK_FILE_SCAN_CLAMAV)s | ||||
|   --clamav-socket "%(ENV_SUPERVISOR_PATH)s/clamav/clamd.ctl" | ||||
|   --req-limit %(ENV_CLAMD_MAX_REQUESTS)s | ||||
| startsecs=30 | ||||
| startretries=3 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| autostart=%(ENV_ZEEK_FILE_SCAN_CLAMAV)s | ||||
| directory=%(ENV_ZEEK_LOG_PATH)s | ||||
| user=sensor | ||||
|  | ||||
| [program:yara] | ||||
| command=/usr/bin/python3 /usr/local/bin/zeek_carve_scanner.py | ||||
|   --start-sleep 20 | ||||
|   --yara %(ENV_ZEEK_FILE_SCAN_YARA)s | ||||
|   --yara-custom-only "%(ENV_EXTRACTED_FILE_YARA_CUSTOM_ONLY)s" | ||||
|   --req-limit %(ENV_YARA_MAX_REQUESTS)s | ||||
| startsecs=30 | ||||
| startretries=3 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| autostart=%(ENV_ZEEK_FILE_SCAN_YARA)s | ||||
| directory=%(ENV_ZEEK_LOG_PATH)s | ||||
| user=sensor | ||||
|  | ||||
| [program:capa] | ||||
| command=/usr/bin/python3 /usr/local/bin/zeek_carve_scanner.py | ||||
|   --start-sleep 20 | ||||
|   --capa %(ENV_ZEEK_FILE_SCAN_CAPA)s | ||||
|   --capa-verbose %(ENV_CAPA_VERBOSE)s | ||||
|   --req-limit %(ENV_CAPA_MAX_REQUESTS)s | ||||
| startsecs=30 | ||||
| startretries=3 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| autostart=%(ENV_ZEEK_FILE_SCAN_CAPA)s | ||||
| directory=%(ENV_ZEEK_LOG_PATH)s | ||||
| user=sensor | ||||
|  | ||||
| [program:malass] | ||||
| command=/usr/bin/python3 /usr/local/bin/zeek_carve_scanner.py | ||||
|   --start-sleep 20 | ||||
|   --malass-host "%(ENV_MALASS_HOST)s" | ||||
|   --malass-port %(ENV_MALASS_PORT)s | ||||
|   --req-limit %(ENV_MALASS_MAX_REQUESTS)s | ||||
| startsecs=30 | ||||
| startretries=3 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| autostart=%(ENV_ZEEK_FILE_SCAN_MALASS)s | ||||
| directory=%(ENV_ZEEK_LOG_PATH)s | ||||
| user=sensor | ||||
|  | ||||
| [program:logger] | ||||
| command=/usr/bin/python3 /usr/local/bin/zeek_carve_logger.py | ||||
|   --start-sleep 10 | ||||
|   --preserve "%(ENV_EXTRACTED_FILE_PRESERVATION)s" | ||||
|   --directory "%(ENV_ZEEK_LOG_PATH)s/extract_files" | ||||
|   --zeek-log "%(ENV_ZEEK_LOG_PATH)s/logs/current/signatures(_carved).log" | ||||
| startsecs=20 | ||||
| startretries=3 | ||||
| stopasgroup=true | ||||
| killasgroup=true | ||||
| autostart=%(ENV_ZEEK_FILE_WATCH)s | ||||
| directory=%(ENV_ZEEK_LOG_PATH)s | ||||
| user=sensor | ||||
| @@ -0,0 +1,79 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # Copyright (c) 2021 Battelle Energy Alliance, LLC.  All rights reserved. | ||||
|  | ||||
| export ARKIME_HTTPS_FLAG="" | ||||
|  | ||||
| if [[ -n $SUPERVISOR_PATH ]] && [[ -r "$SUPERVISOR_PATH"/moloch/config.ini ]]; then | ||||
|  | ||||
|   ARKIME_CONFIG_FILE="$SUPERVISOR_PATH"/moloch/config.ini | ||||
|  | ||||
|   # capture interface(s) | ||||
|   if [[ -n $CAPTURE_INTERFACE ]]; then | ||||
|     # in config.ini multiple interfaces are separated by ; | ||||
|     ARKIME_CAPTURE_INTERFACE="$(echo "$CAPTURE_INTERFACE" | sed "s/,/;/g")" | ||||
|     # place capture interfaces in the config file | ||||
|     sed -r -i "s|(interface)\s*=\s*.*|\1=$ARKIME_CAPTURE_INTERFACE|" "$ARKIME_CONFIG_FILE" | ||||
|   fi | ||||
|  | ||||
|   # stick elasticsearch connection information in moloch config file | ||||
|   if [[ -n $ES_PROTOCOL ]] && [[ -n $ES_HOST ]]; then | ||||
|  | ||||
|     # build elasticsearch URL for moloch-capture | ||||
|     ARKIME_ELASTICSEARCH="${ES_PROTOCOL}://" | ||||
|  | ||||
|     if [[ -n $ES_USERNAME ]] && [[ -n $ES_PASSWORD ]]; then | ||||
|       ARKIME_ELASTICSEARCH+="${ES_USERNAME}:${ES_PASSWORD}@" | ||||
|     fi | ||||
|  | ||||
|     ARKIME_ELASTICSEARCH+="${ES_HOST}" | ||||
|  | ||||
|     if [[ -n $ES_PORT ]]; then | ||||
|       ARKIME_ELASTICSEARCH+=":${ES_PORT}" | ||||
|     else | ||||
|       ARKIME_ELASTICSEARCH+=":9200" | ||||
|     fi | ||||
|  | ||||
|     # place the URL in the config file | ||||
|     sed -r -i "s|(elasticsearch)\s*=\s*.*|\1=$ARKIME_ELASTICSEARCH|" "$ARKIME_CONFIG_FILE" | ||||
|   fi | ||||
|  | ||||
|   # if SSL certificate verification is turned off, supply the --insecure flag | ||||
|   if [[ -n $ES_SSL_VERIFY ]] && [ "$ES_SSL_VERIFY" = none ]; then | ||||
|     export ARKIME_HTTPS_FLAG="--insecure" | ||||
|   fi | ||||
|  | ||||
|   # convert pcap rotation size units (MB to GB) and stick in config file | ||||
|   if [[ -n $PCAP_ROTATE_MEGABYTES ]]; then | ||||
|     PCAP_ROTATE_GIGABYTES=$(echo "($PCAP_ROTATE_MEGABYTES + 1024 - 1)/1024" | bc) | ||||
|     sed -r -i "s/(maxFileSizeG)\s*=\s*.*/\1=$PCAP_ROTATE_GIGABYTES/" "$ARKIME_CONFIG_FILE" | ||||
|   fi | ||||
|  | ||||
|   # convert pcap rotation time units (sec to min) and stick in config file | ||||
|   if [[ -n $PCAP_ROTATE_SECONDS ]]; then | ||||
|     PCAP_ROTATE_MINUTES=$(echo "($PCAP_ROTATE_SECONDS + 60 - 1)/60" | bc) | ||||
|     sed -r -i "s/(maxFileTimeM)\s*=\s*.*/\1=$PCAP_ROTATE_MINUTES/" "$ARKIME_CONFIG_FILE" | ||||
|   fi | ||||
|  | ||||
|   # identify node in session metadata for PCAP reachback | ||||
|   PRIMARY_IP=$(ip route get 255.255.255.255 | grep -Po '(?<=src )(\d{1,3}.){4}' | sed "s/ //g") | ||||
|   export ARKIME_NODE_NAME="$(hostname --long)" | ||||
|   export ARKIME_NODE_HOST="$PRIMARY_IP" | ||||
|  | ||||
|   # get sensor user-owned copies of the moloch-capture lookup files into /opt/sensor/sensor_ctl/moloch | ||||
|   rsync -a --update /opt/moloch/etc/{ipv4-address-space.csv,oui.txt,GeoLite2-Country.mmdb,GeoLite2-ASN.mmdb} /opt/sensor/sensor_ctl/moloch/ | ||||
|   chmod 600 /opt/sensor/sensor_ctl/moloch/{ipv4-address-space.csv,oui.txt,GeoLite2-Country.mmdb,GeoLite2-ASN.mmdb} | ||||
|  | ||||
|   # update the firewall ACL (via ufw) to allow retrieval of packets | ||||
|   sudo --non-interactive /usr/local/bin/ufw_allow_viewer.sh | ||||
|  | ||||
|   # make sure interface flags are set appropriately for capture | ||||
|   if [[ -n $CAPTURE_INTERFACE ]]; then | ||||
|     IFS="," | ||||
|     for IFACE_NAME in $CAPTURE_INTERFACE; do | ||||
|       sudo --non-interactive /usr/local/bin/nic-capture-setup.sh "$IFACE_NAME" >/dev/null 2>&1 | ||||
|     done | ||||
|     unset IFS | ||||
|   fi | ||||
|  | ||||
| fi | ||||
							
								
								
									
										98
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_ctl/supervisor.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										98
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_ctl/supervisor.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,98 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # Copyright (c) 2021 Battelle Energy Alliance, LLC.  All rights reserved. | ||||
|  | ||||
| set -e | ||||
|  | ||||
| CONFIG_DIR="supervisor.d" | ||||
| CONFIG_FILE="supervisord.conf" | ||||
| CONTROL_VARS_FILE="control_vars.conf" | ||||
| CAPTURE_GROUPS_FILE="capture-groups.conf" | ||||
|  | ||||
| function join_by { local IFS="$1"; shift; echo "$*"; } | ||||
|  | ||||
| # Create config files for each capture interface for the various capture programs (tcpdump, netsniff) | ||||
| # so that supervisord can manage instances of each of these programs for each interface. | ||||
| # zeek is now managed by zeekctl (via zeekdeploy.sh) rather than individually by supervisord so that | ||||
| # we can use load balancing | ||||
| function CreateCaptureConfigs() { | ||||
|  | ||||
|   declare -a CAPTURE_PROGS=("tcpdump" "netsniff") | ||||
|  | ||||
|   if [[ -d ./"$CONFIG_DIR" ]]; then | ||||
|     rm -f ./"$CONFIG_DIR"/"$CAPTURE_GROUPS_FILE" | ||||
|  | ||||
|     for PROG in "${CAPTURE_PROGS[@]}"; do | ||||
|       declare -a PROG_GROUP=() | ||||
|  | ||||
|       # remove any old .conf files for this capture program, we'll create them all fresh | ||||
|       rm -f ./"$CONFIG_DIR"/$PROG*.conf | ||||
|       if [[ -n $CAPTURE_INTERFACE ]]; then | ||||
|  | ||||
|         # for each capture interface, expand the capture program's template and substitute for the $IFACE variable | ||||
|         for IFACE in ${CAPTURE_INTERFACE//,/ }; do | ||||
|           if [[ -r ./"$CONFIG_DIR"/$PROG.template ]]; then | ||||
|  | ||||
|             # expand $IFACE into interface name in a new configuration file | ||||
|             export $IFACE | ||||
|             sed -e "s/[$]IFACE/${IFACE}/g" ./"$CONFIG_DIR"/$PROG.template > ./"$CONFIG_DIR"/$PROG-"$IFACE".conf | ||||
|  | ||||
|             # if there needs to be a working directory for supervisord to manage this program, create it now | ||||
|             WORK_DIR=$(eval echo "$(grep ^directory= ./"$CONFIG_DIR"/$PROG-"$IFACE".conf | sed "s/^directory=//" | sed  "s/%(ENV_\(\w*\))s/$\1/")") | ||||
|             mkdir -p "$WORK_DIR" 2>/dev/null || true | ||||
|  | ||||
|             # get new program name for group inclusion | ||||
|             INSTANCE_NAME="$(grep '^\[program:' ./"$CONFIG_DIR"/$PROG-"$IFACE".conf | sed "s/^\[program://" | sed "s/\]$//")" | ||||
|             PROG_GROUP+=($INSTANCE_NAME) | ||||
|  | ||||
|           fi # capture program template exists | ||||
|         done # loop over capture interfaces | ||||
|  | ||||
|       fi # capture interface(s) defined | ||||
|  | ||||
|       if (( ${#PROG_GROUP[@]} )); then | ||||
|         GROUP_PROGS="$(join_by , "${PROG_GROUP[@]}")" | ||||
|         # define group config file | ||||
|         echo "[group:$PROG]" >> ./"$CONFIG_DIR"/"$CAPTURE_GROUPS_FILE" | ||||
|         echo "programs=$GROUP_PROGS" >> ./"$CONFIG_DIR"/"$CAPTURE_GROUPS_FILE" | ||||
|         echo "" >> ./"$CONFIG_DIR"/"$CAPTURE_GROUPS_FILE" | ||||
|       fi | ||||
|  | ||||
|     done # loop over capture programs | ||||
|   fi # config dir exists | ||||
| } | ||||
|  | ||||
| # force-navigate to script directory (containing config file) | ||||
| [[ "$(uname -s)" = 'Darwin' ]] && REALPATH=grealpath || REALPATH=realpath | ||||
| [[ "$(uname -s)" = 'Darwin' ]] && DIRNAME=gdirname || DIRNAME=dirname | ||||
| if ! (type "$REALPATH" && type "$DIRNAME") > /dev/null; then | ||||
|   echo "$(basename "${BASH_SOURCE[0]}") requires $REALPATH and $DIRNAME" | ||||
|   exit 1 | ||||
| fi | ||||
| # SUPERVISOR_PATH is exported to be referenced in supervisord.conf | ||||
| export SUPERVISOR_PATH="$($DIRNAME $($REALPATH -e "${BASH_SOURCE[0]}"))" | ||||
| pushd "$SUPERVISOR_PATH" >/dev/null 2>&1 | ||||
|  | ||||
| source "$CONTROL_VARS_FILE" | ||||
|  | ||||
| CreateCaptureConfigs | ||||
|  | ||||
| mkdir -p "$SUPERVISOR_PATH/"{log,run} | ||||
| rm -f "$SUPERVISOR_PATH/"/log/* | ||||
|  | ||||
| mkdir -p "$ZEEK_LOG_PATH/" 2>/dev/null || true | ||||
| mkdir -p "$PCAP_PATH/"  2>/dev/null || true | ||||
|  | ||||
| if [ -d "$SUPERVISOR_PATH/"/supervisor.init ]; then | ||||
|   popd >/dev/null 2>&1 | ||||
|   set +e | ||||
|   for INIT_FILE in "$SUPERVISOR_PATH/"/supervisor.init/*; do | ||||
|     source "${INIT_FILE}" >/dev/null 2>&1 | ||||
|   done | ||||
|   set -e | ||||
|   pushd "$SUPERVISOR_PATH" >/dev/null 2>&1 | ||||
| fi | ||||
|  | ||||
| supervisord -c "$CONFIG_FILE" | ||||
|  | ||||
| popd >/dev/null 2>&1 | ||||
| @@ -0,0 +1,27 @@ | ||||
| ; 1. requires supervisor | ||||
| ;    - install with Python pip (eg., "pip install supervisor"), | ||||
| ;      or with your package manager (eg., "apt-get install supervisor") | ||||
| ; 2. start supervisord with local supervisor.sh script | ||||
| ; 3. manage services with symlinks to local control.sh | ||||
| ;    - eg., start -> control.sh, stop -> control.sh, etc. | ||||
|  | ||||
| ; see http://supervisord.org/configuration.html for supervisor documentation | ||||
|  | ||||
| [unix_http_server] | ||||
| file=%(ENV_SUPERVISOR_PATH)s/run/supervisor.sock   ; (the path to the socket file for supervisorctl) | ||||
| chmod=0700 | ||||
|  | ||||
| [supervisord] | ||||
| nodaemon=false | ||||
| logfile=%(ENV_SUPERVISOR_PATH)s/log/supervisord.log | ||||
| pidfile=%(ENV_SUPERVISOR_PATH)s/run/supervisord.pid | ||||
| childlogdir=%(ENV_SUPERVISOR_PATH)s/log | ||||
|  | ||||
| [rpcinterface:supervisor] | ||||
| supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface | ||||
|  | ||||
| [supervisorctl] | ||||
| serverurl=unix://%(ENV_SUPERVISOR_PATH)s/run/supervisor.sock | ||||
|  | ||||
| [include] | ||||
| files = %(ENV_SUPERVISOR_PATH)s/supervisor.d/*.conf | ||||
| @@ -0,0 +1,3 @@ | ||||
| from .routes import app | ||||
|  | ||||
| app.run() | ||||
| @@ -0,0 +1,147 @@ | ||||
| # Copyright (c) 2021 Battelle Energy Alliance, LLC.  All rights reserved. | ||||
|  | ||||
| import psutil, time, json, logging, os | ||||
| from .sysquery import sys_service as sys_s | ||||
| from flask import render_template, send_from_directory | ||||
| from flask import Flask | ||||
| from flask_cors import CORS | ||||
|  | ||||
| ''' | ||||
|     Application Configuration | ||||
| ''' | ||||
| APP_ROOT = os.path.dirname(os.path.abspath(__file__))  # refers to application_top | ||||
| APP_STATIC = os.path.join(APP_ROOT, 'static') | ||||
|  | ||||
| app = Flask(__name__) | ||||
| CORS(app) | ||||
|  | ||||
| ''' | ||||
|     Logging configuration | ||||
|     Purpose: Remove the GET requests and other things to just error level records | ||||
| ''' | ||||
| # logging.getLogger('flask_cors').level = logging.DEBUG | ||||
| # log = logging.getLogger('werkzeug') | ||||
| # log.setLevel(logging.ERROR) | ||||
|  | ||||
| ''' | ||||
|     Time Setup | ||||
|     int = seconds | ||||
| ''' | ||||
| period = 1 | ||||
|  | ||||
| ''' | ||||
|     Web Pages | ||||
| ''' | ||||
|  | ||||
|  | ||||
| @app.route('/') | ||||
| def index(): | ||||
|     return render_template('system_block.html') | ||||
|  | ||||
|  | ||||
| @app.route('/buttons') | ||||
| def buttons(): | ||||
|     return render_template('buttons.html') | ||||
|  | ||||
|  | ||||
| ''' | ||||
|     Services | ||||
| ''' | ||||
|  | ||||
|  | ||||
| @app.route('/script_call/<string:script>', methods=['POST']) | ||||
| def activate_service(script): | ||||
|     print(script) | ||||
|     return sys_s.service(script) | ||||
|  | ||||
|  | ||||
| ''' | ||||
|     System Queries | ||||
| ''' | ||||
|  | ||||
|  | ||||
| @app.route('/update', methods=['GET']) | ||||
| def update_stats(): | ||||
|  | ||||
|     req_time = int(time.time()) | ||||
|  | ||||
|     disk_write_data_start = psutil.disk_io_counters(perdisk=False) | ||||
|     io_data_start = psutil.net_io_counters() | ||||
|  | ||||
|     # Some metrics are only reported in values since uptime, | ||||
|     # so sample over a period (in seconds) to get rate. | ||||
|  | ||||
|     time.sleep(period) | ||||
|  | ||||
|     cpu_data = psutil.cpu_percent(interval=None) | ||||
|     ram_data = psutil.virtual_memory() | ||||
|  | ||||
|     # contains all disk data (with total size >= 1GB) | ||||
|     disks_data = list(filter(lambda y: y[1][0] >= 1000000000, [(x.mountpoint, psutil.disk_usage(x.mountpoint)) for x in psutil.disk_partitions()])) | ||||
|  | ||||
|     disks_idx = (req_time // 6) % len(disks_data) | ||||
|  | ||||
|     # contains "currently displayed" disk data (cycles based on time) | ||||
|     disk_mount = disks_data[disks_idx][0] | ||||
|     disk_data = disks_data[disks_idx][1] | ||||
|  | ||||
|     disk_write_data = psutil.disk_io_counters(perdisk=False) | ||||
|     io_data = psutil.net_io_counters() | ||||
|  | ||||
|     data = { | ||||
|         'cpu': { | ||||
|             'percent': cpu_data | ||||
|         }, | ||||
|         'ram': { | ||||
|             'percent': ram_data[2], | ||||
|             'total': ram_data[0], | ||||
|             'used': ram_data[3] | ||||
|         }, | ||||
|         'disks': [], # todo: work in progress | ||||
|         'disk': { | ||||
|             'mount': disk_mount, | ||||
|             'total': disk_data[0], | ||||
|             'used': disk_data[1], | ||||
|             'percent': disk_data[3] | ||||
|         }, | ||||
|         'disk_io': { | ||||
|             'read_bytes_sec': (disk_write_data[2] - disk_write_data_start[2]) | ||||
|                               / period, | ||||
|             'write_bytes_sec': (disk_write_data[3] - disk_write_data_start[3]) | ||||
|                                / period | ||||
|         }, | ||||
|         'net_io': { | ||||
|             'sent_bytes_sec': (io_data[0] - io_data_start[0]) / period, | ||||
|             'received_bytes_sec': (io_data[1] - io_data_start[1]) / period | ||||
|         } | ||||
|     } | ||||
|     for disk_data in disks_data: | ||||
|       data['disks'].append({'mount': disk_data[0], | ||||
|                             'total': disk_data[1][0], | ||||
|                             'used': disk_data[1][1], | ||||
|                             'percent': disk_data[1][3]}) | ||||
|  | ||||
|     return json.dumps(data) | ||||
|  | ||||
|  | ||||
| ''' | ||||
|     Javascript servicing | ||||
| ''' | ||||
|  | ||||
|  | ||||
| @app.route('/load', methods=['GET']) | ||||
| def read_dash(): | ||||
|     with open(os.path.join(APP_STATIC, 'dashboard/dashboard.json')) as f: | ||||
|         data = json.load(f) | ||||
|  | ||||
|     return json.dumps(data) | ||||
|  | ||||
|  | ||||
| @app.route('/plugins/thirdparty/<path:filename>') | ||||
| def serve_static(filename): | ||||
|     dir = os.path.join(APP_STATIC, 'js') | ||||
|     return send_from_directory(dir, filename) | ||||
|  | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     app.run() | ||||
| @@ -0,0 +1,93 @@ | ||||
| /* fallback */ | ||||
| @font-face { | ||||
|     font-family: 'Material Icons'; | ||||
|     font-style: normal; | ||||
|     font-weight: 400; | ||||
|     src: url('../icons/icons.woff2') format('woff2'); | ||||
| } | ||||
|  | ||||
| .material-icons { | ||||
|     font-family: 'Material Icons'; | ||||
|     font-weight: normal; | ||||
|     font-style: normal; | ||||
|     font-size: 24px; | ||||
|     line-height: 1; | ||||
|     letter-spacing: normal; | ||||
|     text-transform: none; | ||||
|     display: inline-block; | ||||
|     white-space: nowrap; | ||||
|     word-wrap: normal; | ||||
|     direction: ltr; | ||||
|     -moz-font-feature-settings: 'liga'; | ||||
|     -moz-osx-font-smoothing: grayscale; | ||||
| } | ||||
|  | ||||
| .square_card { | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
| } | ||||
|  | ||||
| .square_card_start > .mdl-card__title { | ||||
|     color: #fff; | ||||
|     background-color: #00c853; | ||||
| } | ||||
|  | ||||
| .square_card_stop > .mdl-card__title { | ||||
|     color: #fff; | ||||
|     background-color: #aa5500; | ||||
| } | ||||
|  | ||||
| .square_card_status > .mdl-card__title { | ||||
|     color: #fff; | ||||
|     background-color: #42A5F5; | ||||
| } | ||||
|  | ||||
| .square_card_clean > .mdl-card__title { | ||||
|     color: #fff; | ||||
|     background-color: #b71c1c; | ||||
| } | ||||
|  | ||||
| .square_card_system > .mdl-card__title { | ||||
|     color: #fff; | ||||
|     background-color: #26a69a; | ||||
| } | ||||
|  | ||||
| .mdl-grid { | ||||
|     overflow: hidden; | ||||
| } | ||||
|  | ||||
| .mdl-spinner { | ||||
|     width: 284px; | ||||
|     height: 284px; | ||||
|     margin: auto; | ||||
| } | ||||
|  | ||||
| .mdl-spinner__circle { | ||||
|     border-width: 6px; | ||||
| } | ||||
|  | ||||
| .modal { | ||||
|     display: none; | ||||
|     position: absolute; | ||||
|     overflow: auto; | ||||
|     z-index: 1; | ||||
|     margin: auto; | ||||
| } | ||||
|  | ||||
| #myLoading { | ||||
|     background-color: transparent; | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     box-shadow: inset 0 0 490px black; | ||||
|     margin: auto; | ||||
|     margin-top: -75px; | ||||
| } | ||||
|  | ||||
| .center { | ||||
|     margin: auto; | ||||
|     width: 50%; | ||||
|     padding: 10px; | ||||
|     overflow: hidden; | ||||
|     justify-content: center; | ||||
| } | ||||
|  | ||||
							
								
								
									
										2621
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_interface/static/css/freeboard.min.css
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										2621
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_interface/static/css/freeboard.min.css
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,241 @@ | ||||
| { | ||||
|   "version": 1, | ||||
|   "allow_edit": true, | ||||
|   "plugins": [], | ||||
|   "panes": [ | ||||
|     { | ||||
|       "width": 1, | ||||
|       "row": { | ||||
|         "3": 1 | ||||
|       }, | ||||
|       "col": { | ||||
|         "3": 1 | ||||
|       }, | ||||
|       "col_width": 1, | ||||
|       "widgets": [ | ||||
|         { | ||||
|           "type": "text_widget", | ||||
|           "settings": { | ||||
|             "size": "regular", | ||||
|             "value": "datasources[\"Clock\"][\"time_string_value\"]", | ||||
|             "animate": true | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "title": "Disk I/O", | ||||
|       "width": 1, | ||||
|       "row": { | ||||
|         "3": 1 | ||||
|       }, | ||||
|       "col": { | ||||
|         "3": 3 | ||||
|       }, | ||||
|       "col_width": 1, | ||||
|       "widgets": [ | ||||
|         { | ||||
|           "type": "text_widget", | ||||
|           "settings": { | ||||
|             "title": "Bytes Read / sec.", | ||||
|             "size": "regular", | ||||
|             "value": "(datasources[\"System Dashboard Flask\"][\"disk_io\"][\"read_bytes_sec\"] / 1000000).toFixed(3)", | ||||
|             "sparkline": true, | ||||
|             "animate": false, | ||||
|             "units": "MB" | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "type": "text_widget", | ||||
|           "settings": { | ||||
|             "title": "Bytes Written / sec.", | ||||
|             "size": "regular", | ||||
|             "value": "(datasources[\"System Dashboard Flask\"][\"disk_io\"][\"write_bytes_sec\"] / 1000000).toFixed(3)", | ||||
|             "sparkline": true, | ||||
|             "animate": false, | ||||
|             "units": "MB" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "title": "Network I/O", | ||||
|       "width": 1, | ||||
|       "row": { | ||||
|         "3": 1 | ||||
|       }, | ||||
|       "col": { | ||||
|         "3": 2 | ||||
|       }, | ||||
|       "col_width": 1, | ||||
|       "widgets": [ | ||||
|         { | ||||
|           "type": "text_widget", | ||||
|           "settings": { | ||||
|             "title": "Bytes Transmitted / sec.", | ||||
|             "size": "regular", | ||||
|             "value": "(datasources[\"System Dashboard Flask\"][\"net_io\"][\"sent_bytes_sec\"] / 1000000).toFixed(3)", | ||||
|             "sparkline": true, | ||||
|             "animate": false, | ||||
|             "units": "MB" | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "type": "text_widget", | ||||
|           "settings": { | ||||
|             "title": "Bytes Received / sec.", | ||||
|             "size": "regular", | ||||
|             "value": "(datasources[\"System Dashboard Flask\"][\"net_io\"][\"received_bytes_sec\"] / 1000000).toFixed(3)", | ||||
|             "sparkline": true, | ||||
|             "animate": false, | ||||
|             "units": "MB" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "title": "CPU Utilization", | ||||
|       "width": 1, | ||||
|       "row": { | ||||
|         "3": 5 | ||||
|       }, | ||||
|       "col": { | ||||
|         "3": 1 | ||||
|       }, | ||||
|       "col_width": 1, | ||||
|       "widgets": [ | ||||
|         { | ||||
|           "type": "text_widget", | ||||
|           "settings": { | ||||
|             "size": "big", | ||||
|             "value": "datasources[\"System Dashboard Flask\"][\"cpu\"][\"percent\"].toFixed(1)", | ||||
|             "sparkline": true, | ||||
|             "animate": false, | ||||
|             "units": "%" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "title": "RAM Utilization", | ||||
|       "width": 1, | ||||
|       "row": { | ||||
|         "3": 11 | ||||
|       }, | ||||
|       "col": { | ||||
|         "3": 1 | ||||
|       }, | ||||
|       "col_width": 1, | ||||
|       "widgets": [ | ||||
|         { | ||||
|           "type": "text_widget", | ||||
|           "settings": { | ||||
|             "size": "big", | ||||
|             "value": "datasources[\"System Dashboard Flask\"][\"ram\"][\"percent\"].toFixed(1)", | ||||
|             "sparkline": true, | ||||
|             "animate": false, | ||||
|             "units": "%" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "title": "Disk Utilization", | ||||
|       "width": 1, | ||||
|       "row": { | ||||
|         "3": 11 | ||||
|       }, | ||||
|       "col": { | ||||
|         "3": 3 | ||||
|       }, | ||||
|       "col_width": "1", | ||||
|       "widgets": [ | ||||
|         { | ||||
|           "type": "text_widget", | ||||
|           "settings": { | ||||
|             "title": "Used", | ||||
|             "size": "regular", | ||||
|             "value": "(datasources[\"System Dashboard Flask\"][\"disk\"][\"used\"] / (1000 * 1024 * 1024)).toFixed(2)", | ||||
|             "animate": false, | ||||
|             "units": "GB" | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "type": "text_widget", | ||||
|           "settings": { | ||||
|             "title": "Total", | ||||
|             "size": "regular", | ||||
|             "value": "(datasources[\"System Dashboard Flask\"][\"disk\"][\"total\"] / (1000 * 1024 * 1024)).toFixed(2)", | ||||
|             "animate": false, | ||||
|             "units": "GB" | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "type": "text_widget", | ||||
|           "settings": { | ||||
|             "title": "Mount", | ||||
|             "size": "regular", | ||||
|             "value": "datasources[\"System Dashboard Flask\"][\"disk\"][\"mount\"]", | ||||
|             "animate": false, | ||||
|             "units": "" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "title": "RAM Utilization", | ||||
|       "width": 1, | ||||
|       "row": { | ||||
|         "3": 11 | ||||
|       }, | ||||
|       "col": { | ||||
|         "3": 2 | ||||
|       }, | ||||
|       "col_width": 1, | ||||
|       "widgets": [ | ||||
|         { | ||||
|           "type": "text_widget", | ||||
|           "settings": { | ||||
|             "title": "Utilized", | ||||
|             "size": "regular", | ||||
|             "value": "(datasources[\"System Dashboard Flask\"][\"ram\"][\"used\"] / (1000 * 1024 * 1024)).toFixed(2)", | ||||
|             "sparkline": false, | ||||
|             "animate": false, | ||||
|             "units": "GB" | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "type": "text_widget", | ||||
|           "settings": { | ||||
|             "title": "Total", | ||||
|             "size": "regular", | ||||
|             "value": "(datasources[\"System Dashboard Flask\"][\"ram\"][\"total\"] / (1000 * 1024 * 1024)).toFixed(2)", | ||||
|             "sparkline": false, | ||||
|             "animate": true, | ||||
|             "units": "GB" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     } | ||||
|   ], | ||||
|   "datasources": [ | ||||
|     { | ||||
|       "name": "System Dashboard Flask", | ||||
|       "type": "JSON", | ||||
|       "settings": { | ||||
|         "url": "http://127.0.0.1:5000/update", | ||||
|         "use_thingproxy": false, | ||||
|         "refresh": 1, | ||||
|         "method": "GET" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "name": "Clock", | ||||
|       "type": "clock", | ||||
|       "settings": { | ||||
|         "refresh": 1 | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "columns": 3 | ||||
| } | ||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 229 B | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.7 KiB | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 8.6 KiB | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 12 KiB | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.7 KiB | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.7 KiB | 
| @@ -0,0 +1,144 @@ | ||||
| // Copyright (c) 2021 Battelle Energy Alliance, LLC.  All rights reserved. | ||||
|  | ||||
| function start_all() { | ||||
|     var xhttp = new XMLHttpRequest(); | ||||
|     loadingBar('on'); | ||||
|     xhttp.onreadystatechange = function () { | ||||
|         if (this.readyState === 4 && this.status === 200) { | ||||
|             loadingBar('off'); | ||||
|             modal(this.responseText); | ||||
|         } | ||||
|     }; | ||||
|     xhttp.open("POST", "/script_call/start all", true); | ||||
|     xhttp.send(); | ||||
| } | ||||
|  | ||||
|  | ||||
| function stop_all() { | ||||
|     var xhttp = new XMLHttpRequest(); | ||||
|     loadingBar('on'); | ||||
|     xhttp.onreadystatechange = function () { | ||||
|         if (this.readyState === 4 && this.status === 200) { | ||||
|             loadingBar('off'); | ||||
|             modal(this.responseText); | ||||
|         } | ||||
|     }; | ||||
|     xhttp.open("POST", "/script_call/stop all", true); | ||||
|     xhttp.send(); | ||||
|  | ||||
| } | ||||
|  | ||||
| function start_bro() { | ||||
|     var xhttp = new XMLHttpRequest(); | ||||
|     loadingBar('on'); | ||||
|     xhttp.onreadystatechange = function () { | ||||
|         if (this.readyState === 4 && this.status === 200) { | ||||
|             loadingBar('off'); | ||||
|             modal(this.responseText); | ||||
|         } | ||||
|     }; | ||||
|     xhttp.open("POST", "/script_call/start zeek:*", true); | ||||
|     xhttp.send(); | ||||
| } | ||||
|  | ||||
| function stop_bro() { | ||||
|     var xhttp = new XMLHttpRequest(); | ||||
|     loadingBar('on'); | ||||
|     xhttp.onreadystatechange = function () { | ||||
|         if (this.readyState === 4 && this.status === 200) { | ||||
|             loadingBar('off'); | ||||
|             modal(this.responseText); | ||||
|         } | ||||
|     }; | ||||
|     xhttp.open("POST", "/script_call/stop zeek:*", true); | ||||
|     xhttp.send(); | ||||
|  | ||||
| } | ||||
|  | ||||
| function start_tcp() { | ||||
|     var xhttp = new XMLHttpRequest(); | ||||
|     loadingBar('on'); | ||||
|     xhttp.onreadystatechange = function () { | ||||
|         if (this.readyState === 4 && this.status === 200) { | ||||
|             loadingBar('off'); | ||||
|             modal(this.responseText); | ||||
|         } | ||||
|     }; | ||||
|     xhttp.open("POST", "/script_call/start tcpdump:*", true); | ||||
|     xhttp.send(); | ||||
|  | ||||
| } | ||||
|  | ||||
| function stop_tcp() { | ||||
|     var xhttp = new XMLHttpRequest(); | ||||
|     loadingBar('on'); | ||||
|     xhttp.onreadystatechange = function () { | ||||
|         if (this.readyState === 4 && this.status === 200) { | ||||
|             loadingBar('off'); | ||||
|             modal(this.responseText); | ||||
|         } | ||||
|     }; | ||||
|     xhttp.open("POST", "/script_call/stop tcpdump:*", true); | ||||
|     xhttp.send(); | ||||
|  | ||||
| } | ||||
|  | ||||
| function sensor_status() { | ||||
|     var xhttp = new XMLHttpRequest(); | ||||
|     loadingBar('on'); | ||||
|     xhttp.onreadystatechange = function () { | ||||
|         if (this.readyState === 4 && this.status === 200) { | ||||
|             loadingBar('off'); | ||||
|             modal(this.responseText); | ||||
|         } | ||||
|     }; | ||||
|     xhttp.open("POST", "/script_call/status all", true); | ||||
|     xhttp.send(); | ||||
| } | ||||
|  | ||||
| function clean_sensor() { | ||||
|     if (confirm('This will irreversibly remove captured data. Are you sure?')) { | ||||
|         var xhttp = new XMLHttpRequest(); | ||||
|         loadingBar('on'); | ||||
|         xhttp.onreadystatechange = function () { | ||||
|             if (this.readyState === 4 && this.status === 200) { | ||||
|                 loadingBar('off'); | ||||
|                 modal(this.responseText); | ||||
|             } | ||||
|         }; | ||||
|         xhttp.open("POST", "/script_call/clean all", true); | ||||
|         xhttp.send(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| String.prototype.unquoted = function (){return this.replace (/(^")|("$)/g, '')} | ||||
|  | ||||
| function modal(responseText) { | ||||
|  | ||||
|     var modal = document.getElementById('myModal'); | ||||
|     var text = document.getElementById('response_text'); | ||||
|     var closeBtn = document.getElementById("close"); | ||||
|  | ||||
|     modal.style.display = "block"; | ||||
|  | ||||
|     text.innerHTML = responseText.split("\\n").join("<br>").unquoted(); | ||||
|  | ||||
|     closeBtn.onclick = function () { | ||||
|         modal.style.display = "none"; | ||||
|     }; | ||||
|  | ||||
|     window.onclick = function (event) { | ||||
|         if (event.target === modal) { | ||||
|             modal.style.display = "none"; | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| function loadingBar(status) { | ||||
|     var loading = document.getElementById('myLoading'); | ||||
|     if (status === 'on') { | ||||
|         loading.style.display = "block"; | ||||
|     } else { | ||||
|         loading.style.display = "none"; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										1088
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_interface/static/js/freeboard.min.js
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										1088
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_interface/static/js/freeboard.min.js
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										764
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_interface/static/js/freeboard.plugins.min.js
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										764
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_interface/static/js/freeboard.plugins.min.js
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,764 @@ | ||||
| !function () { | ||||
|     var a = function (a, b) { | ||||
|         function c(a) { | ||||
|             e && clearInterval(e), e = setInterval(function () { | ||||
|                 d.updateNow() | ||||
|             }, a) | ||||
|         } | ||||
|  | ||||
|         var d = this, e = null, f = a, g = 0, h = !1; | ||||
|         c(1e3 * f.refresh), this.updateNow = function () { | ||||
|             if (!(g > 1 && !f.use_thingproxy || g > 2)) { | ||||
|                 var a = f.url; | ||||
|                 2 == g && f.use_thingproxy && (a = ("https:" == location.protocol ? "https:" : "http:") + "//thingproxy.freeboard.io/fetch/" + encodeURI(f.url)); | ||||
|                 var c = f.body; | ||||
|                 if (c) try { | ||||
|                     c = JSON.parse(c) | ||||
|                 } catch (e) { | ||||
|                 } | ||||
|                 $.ajax({ | ||||
|                     url: a, | ||||
|                     dataType: 1 == g ? "JSONP" : "JSON", | ||||
|                     type: f.method || "GET", | ||||
|                     data: c, | ||||
|                     beforeSend: function (a) { | ||||
|                         try { | ||||
|                             _.each(f.headers, function (b) { | ||||
|                                 var c = b.name, d = b.value; | ||||
|                                 _.isUndefined(c) || _.isUndefined(d) || a.setRequestHeader(c, d) | ||||
|                             }) | ||||
|                         } catch (b) { | ||||
|                         } | ||||
|                     }, | ||||
|                     success: function (a) { | ||||
|                         h = !0, b(a) | ||||
|                     }, | ||||
|                     error: function (a, b, c) { | ||||
|                         h || (g++, d.updateNow()) | ||||
|                     } | ||||
|                 }) | ||||
|             } | ||||
|         }, this.onDispose = function () { | ||||
|             clearInterval(e), e = null | ||||
|         }, this.onSettingsChanged = function (a) { | ||||
|             h = !1, g = 0, f = a, c(1e3 * f.refresh), d.updateNow() | ||||
|         } | ||||
|     }; | ||||
|     freeboard.loadDatasourcePlugin({ | ||||
|         type_name: "JSON", | ||||
|         settings: [{name: "url", display_name: "URL", type: "text"}, { | ||||
|             name: "use_thingproxy", | ||||
|             display_name: "Try thingproxy", | ||||
|             description: 'A direct JSON connection will be tried first, if that fails, a JSONP connection will be tried. If that fails, you can use thingproxy, which can solve many connection problems to APIs. <a href="https://github.com/Freeboard/thingproxy" target="_blank">More information</a>.', | ||||
|             type: "boolean", | ||||
|             default_value: !0 | ||||
|         }, { | ||||
|             name: "refresh", | ||||
|             display_name: "Refresh Every", | ||||
|             type: "number", | ||||
|             suffix: "seconds", | ||||
|             default_value: 5 | ||||
|         }, { | ||||
|             name: "method", | ||||
|             display_name: "Method", | ||||
|             type: "option", | ||||
|             options: [{name: "GET", value: "GET"}, {name: "POST", value: "POST"}, { | ||||
|                 name: "PUT", | ||||
|                 value: "PUT" | ||||
|             }, {name: "DELETE", value: "DELETE"}] | ||||
|         }, { | ||||
|             name: "body", | ||||
|             display_name: "Body", | ||||
|             type: "text", | ||||
|             description: "The body of the request. Normally only used if method is POST" | ||||
|         }, { | ||||
|             name: "headers", | ||||
|             display_name: "Headers", | ||||
|             type: "array", | ||||
|             settings: [{name: "name", display_name: "Name", type: "text"}, { | ||||
|                 name: "value", | ||||
|                 display_name: "Value", | ||||
|                 type: "text" | ||||
|             }] | ||||
|         }], | ||||
|         newInstance: function (b, c, d) { | ||||
|             c(new a(b, d)) | ||||
|         } | ||||
|     }); | ||||
|     var b = function (a, b) { | ||||
|         function c(a) { | ||||
|             f && clearInterval(f), f = setInterval(function () { | ||||
|                 e.updateNow() | ||||
|             }, a) | ||||
|         } | ||||
|  | ||||
|         function d(a) { | ||||
|             return a.replace(/\w\S*/g, function (a) { | ||||
|                 return a.charAt(0).toUpperCase() + a.substr(1).toLowerCase() | ||||
|             }) | ||||
|         } | ||||
|  | ||||
|         var e = this, f = null, g = a; | ||||
|         c(1e3 * g.refresh), this.updateNow = function () { | ||||
|             $.ajax({ | ||||
|                 url: "http://api.openweathermap.org/data/2.5/weather?APPID=" + g.api_key + "&q=" + encodeURIComponent(g.location) + "&units=" + g.units, | ||||
|                 dataType: "JSONP", | ||||
|                 success: function (a) { | ||||
|                     var c = { | ||||
|                         place_name: a.name, | ||||
|                         sunrise: new Date(1e3 * a.sys.sunrise).toLocaleTimeString(), | ||||
|                         sunset: new Date(1e3 * a.sys.sunset).toLocaleTimeString(), | ||||
|                         conditions: d(a.weather[0].description), | ||||
|                         current_temp: a.main.temp, | ||||
|                         high_temp: a.main.temp_max, | ||||
|                         low_temp: a.main.temp_min, | ||||
|                         pressure: a.main.pressure, | ||||
|                         humidity: a.main.humidity, | ||||
|                         wind_speed: a.wind.speed, | ||||
|                         wind_direction: a.wind.deg | ||||
|                     }; | ||||
|                     b(c) | ||||
|                 }, | ||||
|                 error: function (a, b, c) { | ||||
|                 } | ||||
|             }) | ||||
|         }, this.onDispose = function () { | ||||
|             clearInterval(f), f = null | ||||
|         }, this.onSettingsChanged = function (a) { | ||||
|             g = a, e.updateNow(), c(1e3 * g.refresh) | ||||
|         } | ||||
|     }; | ||||
|     freeboard.loadDatasourcePlugin({ | ||||
|         type_name: "openweathermap", | ||||
|         display_name: "Open Weather Map API", | ||||
|         settings: [{ | ||||
|             name: "api_key", | ||||
|             display_name: "API Key", | ||||
|             type: "text", | ||||
|             description: "Your personal API Key from Open Weather Map" | ||||
|         }, { | ||||
|             name: "location", | ||||
|             display_name: "Location", | ||||
|             type: "text", | ||||
|             description: "Example: London, UK" | ||||
|         }, { | ||||
|             name: "units", | ||||
|             display_name: "Units", | ||||
|             type: "option", | ||||
|             "default": "imperial", | ||||
|             options: [{name: "Imperial", value: "imperial"}, {name: "Metric", value: "metric"}] | ||||
|         }, {name: "refresh", display_name: "Refresh Every", type: "number", suffix: "seconds", default_value: 5}], | ||||
|         newInstance: function (a, c, d) { | ||||
|             c(new b(a, d)) | ||||
|         } | ||||
|     }); | ||||
|     var c = function (a, b) { | ||||
|         function c(a) { | ||||
|             b(a) | ||||
|         } | ||||
|  | ||||
|         var d = this, e = a; | ||||
|         this.updateNow = function () { | ||||
|             dweetio.get_latest_dweet_for(e.thing_id, function (a, b) { | ||||
|                 a || c(b[0].content) | ||||
|             }) | ||||
|         }, this.onDispose = function () { | ||||
|         }, this.onSettingsChanged = function (a) { | ||||
|             dweetio.stop_listening(), e = a, dweetio.listen_for(e.thing_id, function (a) { | ||||
|                 c(a.content) | ||||
|             }) | ||||
|         }, d.onSettingsChanged(a) | ||||
|     }; | ||||
|     freeboard.loadDatasourcePlugin({ | ||||
|         type_name: "dweet_io", | ||||
|         display_name: "Dweet.io", | ||||
|         external_scripts: ["http://dweet.io/client/dweet.io.min.js"], | ||||
|         settings: [{name: "thing_id", display_name: "Thing Name", description: "Example: salty-dog-1", type: "text"}], | ||||
|         newInstance: function (a, b, d) { | ||||
|             b(new c(a, d)) | ||||
|         } | ||||
|     }); | ||||
|     var d = function (a, b) { | ||||
|         function c() { | ||||
|             h.length > 0 ? (i < h.length && (b(h[i]), i++), i >= h.length && g.loop && (i = 0), i < h.length && (e = setTimeout(c, 1e3 * g.refresh))) : b({}) | ||||
|         } | ||||
|  | ||||
|         function d() { | ||||
|             h = [], i = 0, e && (clearTimeout(e), e = null) | ||||
|         } | ||||
|  | ||||
|         var e, f = this, g = a, h = [], i = 0; | ||||
|         this.updateNow = function () { | ||||
|             d(), $.ajax({ | ||||
|                 url: g.datafile, dataType: g.is_jsonp ? "JSONP" : "JSON", success: function (a) { | ||||
|                     h = _.isArray(a) ? a : [], i = 0, c() | ||||
|                 }, error: function (a, b, c) { | ||||
|                 } | ||||
|             }) | ||||
|         }, this.onDispose = function () { | ||||
|             d() | ||||
|         }, this.onSettingsChanged = function (a) { | ||||
|             g = a, f.updateNow() | ||||
|         } | ||||
|     }; | ||||
|     freeboard.loadDatasourcePlugin({ | ||||
|         type_name: "playback", | ||||
|         display_name: "Playback", | ||||
|         settings: [{ | ||||
|             name: "datafile", | ||||
|             display_name: "Data File URL", | ||||
|             type: "text", | ||||
|             description: "A link to a JSON array of data." | ||||
|         }, {name: "is_jsonp", display_name: "Is JSONP", type: "boolean"}, { | ||||
|             name: "loop", | ||||
|             display_name: "Loop", | ||||
|             type: "boolean", | ||||
|             description: "Rewind and loop when finished" | ||||
|         }, {name: "refresh", display_name: "Refresh Every", type: "number", suffix: "seconds", default_value: 5}], | ||||
|         newInstance: function (a, b, c) { | ||||
|             b(new d(a, c)) | ||||
|         } | ||||
|     }); | ||||
|     var e = function (a, b) { | ||||
|         function c() { | ||||
|             e && (clearTimeout(e), e = null) | ||||
|         } | ||||
|  | ||||
|         function d() { | ||||
|             c(), e = setInterval(f.updateNow, 1e3 * g.refresh) | ||||
|         } | ||||
|  | ||||
|         var e, f = this, g = a; | ||||
|         this.updateNow = function () { | ||||
|             var a = new Date, c = { | ||||
|                 numeric_value: a.getTime(), | ||||
|                 full_string_value: a.toLocaleString(), | ||||
|                 date_string_value: a.toLocaleDateString(), | ||||
|                 time_string_value: a.toLocaleTimeString(), | ||||
|                 date_object: a | ||||
|             }; | ||||
|             b(c) | ||||
|         }, this.onDispose = function () { | ||||
|             c() | ||||
|         }, this.onSettingsChanged = function (a) { | ||||
|             g = a, d() | ||||
|         }, d() | ||||
|     }; | ||||
|     freeboard.loadDatasourcePlugin({ | ||||
|         type_name: "clock", | ||||
|         display_name: "Clock", | ||||
|         settings: [{ | ||||
|             name: "refresh", | ||||
|             display_name: "Refresh Every", | ||||
|             type: "number", | ||||
|             suffix: "seconds", | ||||
|             default_value: 1 | ||||
|         }], | ||||
|         newInstance: function (a, b, c) { | ||||
|             b(new e(a, c)) | ||||
|         } | ||||
|     }), freeboard.loadDatasourcePlugin({ | ||||
|         type_name: "meshblu", | ||||
|         display_name: "Octoblu", | ||||
|         description: "app.octoblu.com", | ||||
|         external_scripts: ["http://meshblu.octoblu.com/js/meshblu.js"], | ||||
|         settings: [{ | ||||
|             name: "uuid", | ||||
|             display_name: "UUID", | ||||
|             type: "text", | ||||
|             default_value: "device uuid", | ||||
|             description: "your device UUID", | ||||
|             required: !0 | ||||
|         }, { | ||||
|             name: "token", | ||||
|             display_name: "Token", | ||||
|             type: "text", | ||||
|             default_value: "device token", | ||||
|             description: "your device TOKEN", | ||||
|             required: !0 | ||||
|         }, { | ||||
|             name: "server", | ||||
|             display_name: "Server", | ||||
|             type: "text", | ||||
|             default_value: "meshblu.octoblu.com", | ||||
|             description: "your server", | ||||
|             required: !0 | ||||
|         }, { | ||||
|             name: "port", | ||||
|             display_name: "Port", | ||||
|             type: "number", | ||||
|             default_value: 80, | ||||
|             description: "server port", | ||||
|             required: !0 | ||||
|         }], | ||||
|         newInstance: function (a, b, c) { | ||||
|             b(new f(a, c)) | ||||
|         } | ||||
|     }); | ||||
|     var f = function (a, b) { | ||||
|         function c() { | ||||
|             var a = skynet.createConnection({uuid: e.uuid, token: e.token, server: e.server, port: e.port}); | ||||
|             a.on("ready", function (c) { | ||||
|                 a.on("message", function (a) { | ||||
|                     var c = a; | ||||
|                     b(c) | ||||
|                 }) | ||||
|             }) | ||||
|         } | ||||
|  | ||||
|         var d = this, e = a; | ||||
|         d.onSettingsChanged = function (a) { | ||||
|             e = a | ||||
|         }, d.updateNow = function () { | ||||
|             c() | ||||
|         }, d.onDispose = function () { | ||||
|         } | ||||
|     } | ||||
| }(), function () { | ||||
|     function a(a, b, c) { | ||||
|         var d = $(b).text(); | ||||
|         if (d != a) if ($.isNumeric(a) && $.isNumeric(d)) { | ||||
|             var e = a.toString().split("."), f = 0; | ||||
|             e.length > 1 && (f = e[1].length), e = d.toString().split("."); | ||||
|             var g = 0; | ||||
|             e.length > 1 && (g = e[1].length), jQuery({ | ||||
|                 transitionValue: Number(d), | ||||
|                 precisionValue: g | ||||
|             }).animate({transitionValue: Number(a), precisionValue: f}, { | ||||
|                 duration: c, step: function () { | ||||
|                     $(b).text(this.transitionValue.toFixed(this.precisionValue)) | ||||
|                 }, done: function () { | ||||
|                     $(b).text(a) | ||||
|                 } | ||||
|             }) | ||||
|         } else $(b).text(a) | ||||
|     } | ||||
|  | ||||
|     function b(a, b) { | ||||
|         for (var c = $("<div class='sparkline-legend'></div>"), d = 0; d < b.length; d++) { | ||||
|             var f = e[d % e.length], g = b[d]; | ||||
|             c.append("<div class='sparkline-legend-value'><span style='color:" + f + "'>●</span>" + g + "</div>") | ||||
|         } | ||||
|         a.empty().append(c), freeboard.addStyle(".sparkline-legend", "margin:5px;"), freeboard.addStyle(".sparkline-legend-value", "color:white; font:10px arial,san serif; float:left; overflow:hidden; width:50%;"), freeboard.addStyle(".sparkline-legend-value span", "font-weight:bold; padding-right:5px;") | ||||
|     } | ||||
|  | ||||
|     function c(a, b, c) { | ||||
|         var f = $(a).data().values, g = $(a).data().valueMin, h = $(a).data().valueMax; | ||||
|         f || (f = [], g = void 0, h = void 0); | ||||
|         var i = function (a, b) { | ||||
|             f[b] || (f[b] = []), f[b].length >= d && f[b].shift(), f[b].push(Number(a)), (void 0 === g || g > a) && (g = a), (void 0 === h || a > h) && (h = a) | ||||
|         }; | ||||
|         _.isArray(b) ? _.each(b, i) : i(b, 0), $(a).data().values = f, $(a).data().valueMin = g, $(a).data().valueMax = h; | ||||
|         var j = '<span style="color: {{color}}">●</span> {{y}}', k = !1; | ||||
|         _.each(f, function (b, d) { | ||||
|             $(a).sparkline(b, { | ||||
|                 type: "line", | ||||
|                 composite: k, | ||||
|                 height: "100%", | ||||
|                 width: "100%", | ||||
|                 fillColor: !1, | ||||
|                 lineColor: e[d % e.length], | ||||
|                 lineWidth: 2, | ||||
|                 spotRadius: 3, | ||||
|                 spotColor: !1, | ||||
|                 minSpotColor: "#78AB49", | ||||
|                 maxSpotColor: "#78AB49", | ||||
|                 highlightSpotColor: "#9D3926", | ||||
|                 highlightLineColor: "#9D3926", | ||||
|                 chartRangeMin: g, | ||||
|                 chartRangeMax: h, | ||||
|                 tooltipFormat: c && c[d] ? j + " (" + c[d] + ")" : j | ||||
|             }), k = !0 | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     var d = 100, | ||||
|         e = ["#FF9900", "#FFFFFF", "#B3B4B4", "#6B6B6B", "#28DE28", "#13F7F9", "#E6EE18", "#C41204", "#CA3CB8", "#0B1CFB"], | ||||
|         f = freeboard.getStyleString("values"); | ||||
|     freeboard.addStyle(".widget-big-text", f + "font-size:75px;"), freeboard.addStyle(".tw-display", "width: 100%; height:100%; display:table; table-layout:fixed;"), freeboard.addStyle(".tw-tr", "display:table-row;"), freeboard.addStyle(".tw-tg", "display:table-row-group;"), freeboard.addStyle(".tw-tc", "display:table-caption;"), freeboard.addStyle(".tw-td", "display:table-cell;"), freeboard.addStyle(".tw-value", f + "overflow: hidden;display: inline-block;text-overflow: ellipsis;"), freeboard.addStyle(".tw-unit", "display: inline-block;padding-left: 10px;padding-bottom: 1.1em;vertical-align: bottom;"), freeboard.addStyle(".tw-value-wrapper", "position: relative;vertical-align: middle;height:100%;"), freeboard.addStyle(".tw-sparkline", "height:20px;"); | ||||
|     var g = function (b) { | ||||
|         function d() { | ||||
|             _.isUndefined(e.units) || "" == e.units ? h.css("max-width", "100%") : h.css("max-width", f.innerWidth() - i.outerWidth(!0) + "px") | ||||
|         } | ||||
|  | ||||
|         var e = b, f = $('<div class="tw-display"></div>'), g = $('<h2 class="section-title tw-title tw-td"></h2>'), | ||||
|             h = $('<div class="tw-value"></div>'), i = $('<div class="tw-unit"></div>'), | ||||
|             j = $('<div class="tw-sparkline tw-td"></div>'); | ||||
|         this.render = function (a) { | ||||
|             $(a).empty(), $(f).append($('<div class="tw-tr"></div>').append(g)).append($('<div class="tw-tr"></div>').append($('<div class="tw-value-wrapper tw-td"></div>').append(h).append(i))).append($('<div class="tw-tr"></div>').append(j)), $(a).append(f), d() | ||||
|         }, this.onSettingsChanged = function (a) { | ||||
|             e = a; | ||||
|             var b = !_.isUndefined(a.title) && "" != a.title, c = !_.isUndefined(a.units) && "" != a.units; | ||||
|             a.sparkline ? j.attr("style", null) : (delete j.data().values, j.empty(), j.hide()), b ? (g.html(_.isUndefined(a.title) ? "" : a.title), g.attr("style", null)) : (g.empty(), g.hide()), c ? (i.html(_.isUndefined(a.units) ? "" : a.units), i.attr("style", null)) : (i.empty(), i.hide()); | ||||
|             var f = 30; | ||||
|             "big" == a.size && (f = 75, a.sparkline && (f = 60)), h.css({"font-size": f + "px"}), d() | ||||
|         }, this.onSizeChanged = function () { | ||||
|             d() | ||||
|         }, this.onCalculatedValueChanged = function (b, d) { | ||||
|             "value" == b && (e.animate ? a(d, h, 500) : h.text(d), e.sparkline && c(j, d)) | ||||
|         }, this.onDispose = function () { | ||||
|         }, this.getHeight = function () { | ||||
|             return "big" == e.size || e.sparkline ? 2 : 1 | ||||
|         }, this.onSettingsChanged(b) | ||||
|     }; | ||||
|     freeboard.loadWidgetPlugin({ | ||||
|         type_name: "text_widget", | ||||
|         display_name: "Text", | ||||
|         external_scripts: ["jquery.sparkline.min.js"], | ||||
|         settings: [{name: "title", display_name: "Title", type: "text"}, { | ||||
|             name: "size", | ||||
|             display_name: "Size", | ||||
|             type: "option", | ||||
|             options: [{name: "Regular", value: "regular"}, {name: "Big", value: "big"}] | ||||
|         }, {name: "value", display_name: "Value", type: "calculated"}, { | ||||
|             name: "sparkline", | ||||
|             display_name: "Include Sparkline", | ||||
|             type: "boolean" | ||||
|         }, {name: "animate", display_name: "Animate Value Changes", type: "boolean", default_value: !0}, { | ||||
|             name: "units", | ||||
|             display_name: "Units", | ||||
|             type: "text" | ||||
|         }], | ||||
|         newInstance: function (a, b) { | ||||
|             b(new g(a)) | ||||
|         } | ||||
|     }); | ||||
|     var h = 0; | ||||
|     freeboard.addStyle(".gauge-widget-wrapper", "width: 100%;text-align: center;"), freeboard.addStyle(".gauge-widget", "width:200px;height:160px;display:inline-block;"); | ||||
|     var i = function (a) { | ||||
|         function b() { | ||||
|             g && (f.empty(), c = new JustGage({ | ||||
|                 id: d, | ||||
|                 value: _.isUndefined(i.min_value) ? 0 : i.min_value, | ||||
|                 min: _.isUndefined(i.min_value) ? 0 : i.min_value, | ||||
|                 max: _.isUndefined(i.max_value) ? 0 : i.max_value, | ||||
|                 label: i.units, | ||||
|                 showInnerShadow: !1, | ||||
|                 valueFontColor: "#d3d4d4" | ||||
|             })) | ||||
|         } | ||||
|  | ||||
|         var c, d = "gauge-" + h++, e = $('<h2 class="section-title"></h2>'), | ||||
|             f = $('<div class="gauge-widget" id="' + d + '"></div>'), g = !1, i = a; | ||||
|         this.render = function (a) { | ||||
|             g = !0, $(a).append(e).append($('<div class="gauge-widget-wrapper"></div>').append(f)), b() | ||||
|         }, this.onSettingsChanged = function (a) { | ||||
|             a.min_value != i.min_value || a.max_value != i.max_value || a.units != i.units ? (i = a, b()) : i = a, e.html(a.title) | ||||
|         }, this.onCalculatedValueChanged = function (a, b) { | ||||
|             _.isUndefined(c) || c.refresh(Number(b)) | ||||
|         }, this.onDispose = function () { | ||||
|         }, this.getHeight = function () { | ||||
|             return 3 | ||||
|         }, this.onSettingsChanged(a) | ||||
|     }; | ||||
|     freeboard.loadWidgetPlugin({ | ||||
|         type_name: "gauge", | ||||
|         display_name: "Gauge", | ||||
|         external_scripts: ["plugins/thirdparty/raphael.2.1.0.min.js", "plugins/thirdparty/justgage.1.0.1.js"], | ||||
|         settings: [{name: "title", display_name: "Title", type: "text"}, { | ||||
|             name: "value", | ||||
|             display_name: "Value", | ||||
|             type: "calculated" | ||||
|         }, {name: "units", display_name: "Units", type: "text"}, { | ||||
|             name: "min_value", | ||||
|             display_name: "Minimum", | ||||
|             type: "text", | ||||
|             default_value: 0 | ||||
|         }, {name: "max_value", display_name: "Maximum", type: "text", default_value: 100}], | ||||
|         newInstance: function (a, b) { | ||||
|             b(new i(a)) | ||||
|         } | ||||
|     }), freeboard.addStyle(".sparkline", "width:100%;height: 75px;"); | ||||
|     var j = function (a) { | ||||
|         var d = $('<h2 class="section-title"></h2>'), e = $('<div class="sparkline"></div>'), f = $("<div></div>"), | ||||
|             g = a; | ||||
|         this.render = function (a) { | ||||
|             $(a).append(d).append(e).append(f) | ||||
|         }, this.onSettingsChanged = function (a) { | ||||
|             g = a, d.html(_.isUndefined(a.title) ? "" : a.title), a.include_legend && b(f, a.legend.split(",")) | ||||
|         }, this.onCalculatedValueChanged = function (a, b) { | ||||
|             g.legend ? c(e, b, g.legend.split(",")) : c(e, b) | ||||
|         }, this.onDispose = function () { | ||||
|         }, this.getHeight = function () { | ||||
|             var a = 0; | ||||
|             if (g.include_legend && g.legend) { | ||||
|                 var b = g.legend.split(",").length; | ||||
|                 b > 4 ? a = .5 * Math.floor((b - 1) / 4) : b && (a = .5) | ||||
|             } | ||||
|             return 2 + a | ||||
|         }, this.onSettingsChanged(a) | ||||
|     }; | ||||
|     freeboard.loadWidgetPlugin({ | ||||
|         type_name: "sparkline", | ||||
|         display_name: "Sparkline", | ||||
|         external_scripts: ["jquery.sparkline.min.js"], | ||||
|         settings: [{name: "title", display_name: "Title", type: "text"}, { | ||||
|             name: "value", | ||||
|             display_name: "Value", | ||||
|             type: "calculated", | ||||
|             multi_input: "true" | ||||
|         }, {name: "include_legend", display_name: "Include Legend", type: "boolean"}, { | ||||
|             name: "legend", | ||||
|             display_name: "Legend", | ||||
|             type: "text", | ||||
|             description: "Comma-separated for multiple sparklines" | ||||
|         }], | ||||
|         newInstance: function (a, b) { | ||||
|             b(new j(a)) | ||||
|         } | ||||
|     }), freeboard.addStyle("div.pointer-value", "position:absolute;height:95px;margin: auto;top: 0px;bottom: 0px;width: 100%;text-align:center;"); | ||||
|     var k = function (a) { | ||||
|         function b(a) { | ||||
|             if (!a || a.length < 2) return []; | ||||
|             var b = []; | ||||
|             b.push(["m", a[0], a[1]]); | ||||
|             for (var c = 2; c < a.length; c += 2) b.push(["l", a[c], a[c + 1]]); | ||||
|             return b.push(["z"]), b | ||||
|         } | ||||
|  | ||||
|         var c, d, e, f, g = 3, h = 0, i = $('<div class="widget-big-text"></div>'), j = $("<div></div>"); | ||||
|         this.render = function (a) { | ||||
|             e = $(a).width(), f = $(a).height(); | ||||
|             var h = Math.min(e, f) / 2 - 2 * g; | ||||
|             c = Raphael($(a).get()[0], e, f); | ||||
|             var k = c.circle(e / 2, f / 2, h); | ||||
|             k.attr("stroke", "#FF9900"), k.attr("stroke-width", g), d = c.path(b([e / 2, f / 2 - h + g, 15, 20, -30, 0])), d.attr("stroke-width", 0), d.attr("fill", "#fff"), $(a).append($('<div class="pointer-value"></div>').append(i).append(j)) | ||||
|         }, this.onSettingsChanged = function (a) { | ||||
|             j.html(a.units) | ||||
|         }, this.onCalculatedValueChanged = function (a, b) { | ||||
|             if ("direction" == a) { | ||||
|                 if (!_.isUndefined(d)) { | ||||
|                     d.animate({transform: "r" + b + "," + e / 2 + "," + f / 2}, 250, "bounce") | ||||
|                 } | ||||
|                 h = b | ||||
|             } else "value_text" == a && i.html(b) | ||||
|         }, this.onDispose = function () { | ||||
|         }, this.getHeight = function () { | ||||
|             return 4 | ||||
|         }, this.onSettingsChanged(a) | ||||
|     }; | ||||
|     freeboard.loadWidgetPlugin({ | ||||
|         type_name: "pointer", | ||||
|         display_name: "Pointer", | ||||
|         external_scripts: ["plugins/thirdparty/raphael.2.1.0.min.js"], | ||||
|         settings: [{ | ||||
|             name: "direction", | ||||
|             display_name: "Direction", | ||||
|             type: "calculated", | ||||
|             description: "In degrees" | ||||
|         }, {name: "value_text", display_name: "Value Text", type: "calculated"}, { | ||||
|             name: "units", | ||||
|             display_name: "Units", | ||||
|             type: "text" | ||||
|         }], | ||||
|         newInstance: function (a, b) { | ||||
|             b(new k(a)) | ||||
|         } | ||||
|     }); | ||||
|     var l = function (a) { | ||||
|         function b() { | ||||
|             e && (clearInterval(e), e = null) | ||||
|         } | ||||
|  | ||||
|         function c() { | ||||
|             if (d && f) { | ||||
|                 var a = f + (-1 == f.indexOf("?") ? "?" : "&") + Date.now(); | ||||
|                 $(d).css({"background-image": "url(" + a + ")"}) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         var d, e, f; | ||||
|         this.render = function (a) { | ||||
|             $(a).css({ | ||||
|                 width: "100%", | ||||
|                 height: "100%", | ||||
|                 "background-size": "cover", | ||||
|                 "background-position": "center" | ||||
|             }), d = a | ||||
|         }, this.onSettingsChanged = function (a) { | ||||
|             b(), a.refresh && a.refresh > 0 && (e = setInterval(c, 1e3 * Number(a.refresh))) | ||||
|         }, this.onCalculatedValueChanged = function (a, b) { | ||||
|             "src" == a && (f = b), c() | ||||
|         }, this.onDispose = function () { | ||||
|             b() | ||||
|         }, this.getHeight = function () { | ||||
|             return 4 | ||||
|         }, this.onSettingsChanged(a) | ||||
|     }; | ||||
|     freeboard.loadWidgetPlugin({ | ||||
|         type_name: "picture", | ||||
|         display_name: "Picture", | ||||
|         fill_size: !0, | ||||
|         settings: [{name: "src", display_name: "Image URL", type: "calculated"}, { | ||||
|             type: "number", | ||||
|             display_name: "Refresh every", | ||||
|             name: "refresh", | ||||
|             suffix: "seconds", | ||||
|             description: "Leave blank if the image doesn't need to be refreshed" | ||||
|         }], | ||||
|         newInstance: function (a, b) { | ||||
|             b(new l(a)) | ||||
|         } | ||||
|     }), freeboard.addStyle(".indicator-light", "border-radius:50%;width:22px;height:22px;border:2px solid #3d3d3d;margin-top:5px;float:left;background-color:#222;margin-right:10px;"), freeboard.addStyle(".indicator-light.on", "background-color:#FFC773;box-shadow: 0px 0px 15px #FF9900;border-color:#FDF1DF;"), freeboard.addStyle(".indicator-text", "margin-top:10px;"); | ||||
|     var m = function (a) { | ||||
|         function b() { | ||||
|             g.toggleClass("on", i), i ? f.text(_.isUndefined(c) ? _.isUndefined(h.on_text) ? "" : h.on_text : c) : f.text(_.isUndefined(d) ? _.isUndefined(h.off_text) ? "" : h.off_text : d) | ||||
|         } | ||||
|  | ||||
|         var c, d, e = $('<h2 class="section-title"></h2>'), f = $('<div class="indicator-text"></div>'), | ||||
|             g = $('<div class="indicator-light"></div>'), h = a, i = !1; | ||||
|         this.render = function (a) { | ||||
|             $(a).append(e).append(g).append(f) | ||||
|         }, this.onSettingsChanged = function (a) { | ||||
|             h = a, e.html(_.isUndefined(a.title) ? "" : a.title), b() | ||||
|         }, this.onCalculatedValueChanged = function (a, e) { | ||||
|             "value" == a && (i = Boolean(e)), "on_text" == a && (c = e), "off_text" == a && (d = e), b() | ||||
|         }, this.onDispose = function () { | ||||
|         }, this.getHeight = function () { | ||||
|             return 1 | ||||
|         }, this.onSettingsChanged(a) | ||||
|     }; | ||||
|     freeboard.loadWidgetPlugin({ | ||||
|         type_name: "indicator", | ||||
|         display_name: "Indicator Light", | ||||
|         settings: [{name: "title", display_name: "Title", type: "text"}, { | ||||
|             name: "value", | ||||
|             display_name: "Value", | ||||
|             type: "calculated" | ||||
|         }, {name: "on_text", display_name: "On Text", type: "calculated"}, { | ||||
|             name: "off_text", | ||||
|             display_name: "Off Text", | ||||
|             type: "calculated" | ||||
|         }], | ||||
|         newInstance: function (a, b) { | ||||
|             b(new m(a)) | ||||
|         } | ||||
|     }), freeboard.addStyle(".gm-style-cc a", "text-shadow:none;"); | ||||
|     var n = function (a) { | ||||
|         function b() { | ||||
|             if (c && d && f.lat && f.lon) { | ||||
|                 var a = new google.maps.LatLng(f.lat, f.lon); | ||||
|                 d.setPosition(a), c.panTo(a) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         var c, d, e = a, f = {}; | ||||
|         this.render = function (a) { | ||||
|             function e() { | ||||
|                 var e = { | ||||
|                     zoom: 13, | ||||
|                     center: new google.maps.LatLng(37.235, -115.811111), | ||||
|                     disableDefaultUI: !0, | ||||
|                     draggable: !1, | ||||
|                     styles: [{ | ||||
|                         featureType: "water", | ||||
|                         elementType: "geometry", | ||||
|                         stylers: [{color: "#2a2a2a"}] | ||||
|                     }, { | ||||
|                         featureType: "landscape", | ||||
|                         elementType: "geometry", | ||||
|                         stylers: [{color: "#000000"}, {lightness: 20}] | ||||
|                     }, { | ||||
|                         featureType: "road.highway", | ||||
|                         elementType: "geometry.fill", | ||||
|                         stylers: [{color: "#000000"}, {lightness: 17}] | ||||
|                     }, { | ||||
|                         featureType: "road.highway", | ||||
|                         elementType: "geometry.stroke", | ||||
|                         stylers: [{color: "#000000"}, {lightness: 29}, {weight: .2}] | ||||
|                     }, { | ||||
|                         featureType: "road.arterial", | ||||
|                         elementType: "geometry", | ||||
|                         stylers: [{color: "#000000"}, {lightness: 18}] | ||||
|                     }, { | ||||
|                         featureType: "road.local", | ||||
|                         elementType: "geometry", | ||||
|                         stylers: [{color: "#000000"}, {lightness: 16}] | ||||
|                     }, { | ||||
|                         featureType: "poi", | ||||
|                         elementType: "geometry", | ||||
|                         stylers: [{color: "#000000"}, {lightness: 21}] | ||||
|                     }, { | ||||
|                         elementType: "labels.text.stroke", | ||||
|                         stylers: [{visibility: "on"}, {color: "#000000"}, {lightness: 16}] | ||||
|                     }, { | ||||
|                         elementType: "labels.text.fill", | ||||
|                         stylers: [{saturation: 36}, {color: "#000000"}, {lightness: 40}] | ||||
|                     }, {elementType: "labels.icon", stylers: [{visibility: "off"}]}, { | ||||
|                         featureType: "transit", | ||||
|                         elementType: "geometry", | ||||
|                         stylers: [{color: "#000000"}, {lightness: 19}] | ||||
|                     }, { | ||||
|                         featureType: "administrative", | ||||
|                         elementType: "geometry.fill", | ||||
|                         stylers: [{color: "#000000"}, {lightness: 20}] | ||||
|                     }, { | ||||
|                         featureType: "administrative", | ||||
|                         elementType: "geometry.stroke", | ||||
|                         stylers: [{color: "#000000"}, {lightness: 17}, {weight: 1.2}] | ||||
|                     }] | ||||
|                 }; | ||||
|                 c = new google.maps.Map(a, e), google.maps.event.addDomListener(a, "mouseenter", function (a) { | ||||
|                     a.cancelBubble = !0, c.hover || (c.hover = !0, c.setOptions({zoomControl: !0})) | ||||
|                 }), google.maps.event.addDomListener(a, "mouseleave", function (a) { | ||||
|                     c.hover && (c.setOptions({zoomControl: !1}), c.hover = !1) | ||||
|                 }), d = new google.maps.Marker({map: c}), b() | ||||
|             } | ||||
|  | ||||
|             window.google && window.google.maps ? e() : (window.gmap_initialize = e, head.js("https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=gmap_initialize")) | ||||
|         }, this.onSettingsChanged = function (a) { | ||||
|             e = a | ||||
|         }, this.onCalculatedValueChanged = function (a, c) { | ||||
|             "lat" == a ? f.lat = c : "lon" == a && (f.lon = c), b() | ||||
|         }, this.onDispose = function () { | ||||
|         }, this.getHeight = function () { | ||||
|             return 4 | ||||
|         }, this.onSettingsChanged(a) | ||||
|     }; | ||||
|     freeboard.loadWidgetPlugin({ | ||||
|         type_name: "google_map", | ||||
|         display_name: "Google Map", | ||||
|         fill_size: !0, | ||||
|         settings: [{name: "lat", display_name: "Latitude", type: "calculated"}, { | ||||
|             name: "lon", | ||||
|             display_name: "Longitude", | ||||
|             type: "calculated" | ||||
|         }], | ||||
|         newInstance: function (a, b) { | ||||
|             b(new n(a)) | ||||
|         } | ||||
|     }), freeboard.addStyle(".html-widget", "white-space:normal;width:100%;height:100%"); | ||||
|     var o = function (a) { | ||||
|         var b = $('<div class="html-widget"></div>'), c = a; | ||||
|         this.render = function (a) { | ||||
|             $(a).append(b) | ||||
|         }, this.onSettingsChanged = function (a) { | ||||
|             c = a | ||||
|         }, this.onCalculatedValueChanged = function (a, c) { | ||||
|             "html" == a && b.html(c) | ||||
|         }, this.onDispose = function () { | ||||
|         }, this.getHeight = function () { | ||||
|             return Number(c.height) | ||||
|         }, this.onSettingsChanged(a) | ||||
|     }; | ||||
|     freeboard.loadWidgetPlugin({ | ||||
|         type_name: "html", | ||||
|         display_name: "HTML", | ||||
|         fill_size: !0, | ||||
|         settings: [{ | ||||
|             name: "html", | ||||
|             display_name: "HTML", | ||||
|             type: "calculated", | ||||
|             description: "Can be literal HTML, or javascript that outputs HTML." | ||||
|         }, { | ||||
|             name: "height", | ||||
|             display_name: "Height Blocks", | ||||
|             type: "number", | ||||
|             default_value: 4, | ||||
|             description: "A height block is around 60 pixels" | ||||
|         }], | ||||
|         newInstance: function (a, b) { | ||||
|             b(new o(a)) | ||||
|         } | ||||
|     }) | ||||
| }(); | ||||
							
								
								
									
										16054
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_interface/static/js/freeboard.thirdparty.min.js
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										16054
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_interface/static/js/freeboard.thirdparty.min.js
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1851
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_interface/static/js/freeboard_plugins.min.js
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										1851
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_interface/static/js/freeboard_plugins.min.js
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										893
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_interface/static/js/jquery.sparkline.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										893
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_interface/static/js/jquery.sparkline.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,893 @@ | ||||
| /* jquery.sparkline 2.1.2 - http://omnipotent.net/jquery.sparkline/ | ||||
| ** Licensed under the New BSD License - see above site for details */ | ||||
|  | ||||
| (function (a, b, c) { | ||||
|     (function (a) { | ||||
|         typeof define == "function" && define.amd ? define(["jquery"], a) : jQuery && !jQuery.fn.sparkline && a(jQuery) | ||||
|     })(function (d) { | ||||
|         "use strict"; | ||||
|         var e = {}, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, | ||||
|             L = 0; | ||||
|         f = function () { | ||||
|             return { | ||||
|                 common: { | ||||
|                     type: "line", | ||||
|                     lineColor: "#00f", | ||||
|                     fillColor: "#cdf", | ||||
|                     defaultPixelsPerValue: 3, | ||||
|                     width: "auto", | ||||
|                     height: "auto", | ||||
|                     composite: !1, | ||||
|                     tagValuesAttribute: "values", | ||||
|                     tagOptionsPrefix: "spark", | ||||
|                     enableTagOptions: !1, | ||||
|                     enableHighlight: !0, | ||||
|                     highlightLighten: 1.4, | ||||
|                     tooltipSkipNull: !0, | ||||
|                     tooltipPrefix: "", | ||||
|                     tooltipSuffix: "", | ||||
|                     disableHiddenCheck: !1, | ||||
|                     numberFormatter: !1, | ||||
|                     numberDigitGroupCount: 3, | ||||
|                     numberDigitGroupSep: ",", | ||||
|                     numberDecimalMark: ".", | ||||
|                     disableTooltips: !1, | ||||
|                     disableInteraction: !1 | ||||
|                 }, | ||||
|                 line: { | ||||
|                     spotColor: "#f80", | ||||
|                     highlightSpotColor: "#5f5", | ||||
|                     highlightLineColor: "#f22", | ||||
|                     spotRadius: 1.5, | ||||
|                     minSpotColor: "#f80", | ||||
|                     maxSpotColor: "#f80", | ||||
|                     lineWidth: 1, | ||||
|                     normalRangeMin: c, | ||||
|                     normalRangeMax: c, | ||||
|                     normalRangeColor: "#ccc", | ||||
|                     drawNormalOnTop: !1, | ||||
|                     chartRangeMin: c, | ||||
|                     chartRangeMax: c, | ||||
|                     chartRangeMinX: c, | ||||
|                     chartRangeMaxX: c, | ||||
|                     tooltipFormat: new h('<span style="color: {{color}}">●</span> {{prefix}}{{y}}{{suffix}}') | ||||
|                 }, | ||||
|                 bar: { | ||||
|                     barColor: "#3366cc", | ||||
|                     negBarColor: "#f44", | ||||
|                     stackedBarColor: ["#3366cc", "#dc3912", "#ff9900", "#109618", "#66aa00", "#dd4477", "#0099c6", "#990099"], | ||||
|                     zeroColor: c, | ||||
|                     nullColor: c, | ||||
|                     zeroAxis: !0, | ||||
|                     barWidth: 4, | ||||
|                     barSpacing: 1, | ||||
|                     chartRangeMax: c, | ||||
|                     chartRangeMin: c, | ||||
|                     chartRangeClip: !1, | ||||
|                     colorMap: c, | ||||
|                     tooltipFormat: new h('<span style="color: {{color}}">●</span> {{prefix}}{{value}}{{suffix}}') | ||||
|                 }, | ||||
|                 tristate: { | ||||
|                     barWidth: 4, | ||||
|                     barSpacing: 1, | ||||
|                     posBarColor: "#6f6", | ||||
|                     negBarColor: "#f44", | ||||
|                     zeroBarColor: "#999", | ||||
|                     colorMap: {}, | ||||
|                     tooltipFormat: new h('<span style="color: {{color}}">●</span> {{value:map}}'), | ||||
|                     tooltipValueLookups: {map: {"-1": "Loss", 0: "Draw", 1: "Win"}} | ||||
|                 }, | ||||
|                 discrete: { | ||||
|                     lineHeight: "auto", | ||||
|                     thresholdColor: c, | ||||
|                     thresholdValue: 0, | ||||
|                     chartRangeMax: c, | ||||
|                     chartRangeMin: c, | ||||
|                     chartRangeClip: !1, | ||||
|                     tooltipFormat: new h("{{prefix}}{{value}}{{suffix}}") | ||||
|                 }, | ||||
|                 bullet: { | ||||
|                     targetColor: "#f33", | ||||
|                     targetWidth: 3, | ||||
|                     performanceColor: "#33f", | ||||
|                     rangeColors: ["#d3dafe", "#a8b6ff", "#7f94ff"], | ||||
|                     base: c, | ||||
|                     tooltipFormat: new h("{{fieldkey:fields}} - {{value}}"), | ||||
|                     tooltipValueLookups: {fields: {r: "Range", p: "Performance", t: "Target"}} | ||||
|                 }, | ||||
|                 pie: { | ||||
|                     offset: 0, | ||||
|                     sliceColors: ["#3366cc", "#dc3912", "#ff9900", "#109618", "#66aa00", "#dd4477", "#0099c6", "#990099"], | ||||
|                     borderWidth: 0, | ||||
|                     borderColor: "#000", | ||||
|                     tooltipFormat: new h('<span style="color: {{color}}">●</span> {{value}} ({{percent.1}}%)') | ||||
|                 }, | ||||
|                 box: { | ||||
|                     raw: !1, | ||||
|                     boxLineColor: "#000", | ||||
|                     boxFillColor: "#cdf", | ||||
|                     whiskerColor: "#000", | ||||
|                     outlierLineColor: "#333", | ||||
|                     outlierFillColor: "#fff", | ||||
|                     medianColor: "#f00", | ||||
|                     showOutliers: !0, | ||||
|                     outlierIQR: 1.5, | ||||
|                     spotRadius: 1.5, | ||||
|                     target: c, | ||||
|                     targetColor: "#4a2", | ||||
|                     chartRangeMax: c, | ||||
|                     chartRangeMin: c, | ||||
|                     tooltipFormat: new h("{{field:fields}}: {{value}}"), | ||||
|                     tooltipFormatFieldlistKey: "field", | ||||
|                     tooltipValueLookups: { | ||||
|                         fields: { | ||||
|                             lq: "Lower Quartile", | ||||
|                             med: "Median", | ||||
|                             uq: "Upper Quartile", | ||||
|                             lo: "Left Outlier", | ||||
|                             ro: "Right Outlier", | ||||
|                             lw: "Left Whisker", | ||||
|                             rw: "Right Whisker" | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }, E = '.jqstooltip { position: absolute;left: 0px;top: 0px;visibility: hidden;background: rgb(0, 0, 0) transparent;background-color: rgba(0,0,0,0.6);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)";color: white;font: 10px arial, san serif;text-align: left;white-space: nowrap;padding: 5px;border: 1px solid white;z-index: 10000;}.jqsfield { color: white;font: 10px arial, san serif;text-align: left;}', g = function () { | ||||
|             var a, b; | ||||
|             return a = function () { | ||||
|                 this.init.apply(this, arguments) | ||||
|             }, arguments.length > 1 ? (arguments[0] ? (a.prototype = d.extend(new arguments[0], arguments[arguments.length - 1]), a._super = arguments[0].prototype) : a.prototype = arguments[arguments.length - 1], arguments.length > 2 && (b = Array.prototype.slice.call(arguments, 1, -1), b.unshift(a.prototype), d.extend.apply(d, b))) : a.prototype = arguments[0], a.prototype.cls = a, a | ||||
|         }, d.SPFormatClass = h = g({ | ||||
|             fre: /\{\{([\w.]+?)(:(.+?))?\}\}/g, precre: /(\w+)\.(\d+)/, init: function (a, b) { | ||||
|                 this.format = a, this.fclass = b | ||||
|             }, render: function (a, b, d) { | ||||
|                 var e = this, f = a, g, h, i, j, k; | ||||
|                 return this.format.replace(this.fre, function () { | ||||
|                     var a; | ||||
|                     return h = arguments[1], i = arguments[3], g = e.precre.exec(h), g ? (k = g[2], h = g[1]) : k = !1, j = f[h], j === c ? "" : i && b && b[i] ? (a = b[i], a.get ? b[i].get(j) || j : b[i][j] || j) : (n(j) && (d.get("numberFormatter") ? j = d.get("numberFormatter")(j) : j = s(j, k, d.get("numberDigitGroupCount"), d.get("numberDigitGroupSep"), d.get("numberDecimalMark"))), j) | ||||
|                 }) | ||||
|             } | ||||
|         }), d.spformat = function (a, b) { | ||||
|             return new h(a, b) | ||||
|         }, i = function (a, b, c) { | ||||
|             return a < b ? b : a > c ? c : a | ||||
|         }, j = function (a, c) { | ||||
|             var d; | ||||
|             return c === 2 ? (d = b.floor(a.length / 2), a.length % 2 ? a[d] : (a[d - 1] + a[d]) / 2) : a.length % 2 ? (d = (a.length * c + c) / 4, d % 1 ? (a[b.floor(d)] + a[b.floor(d) - 1]) / 2 : a[d - 1]) : (d = (a.length * c + 2) / 4, d % 1 ? (a[b.floor(d)] + a[b.floor(d) - 1]) / 2 : a[d - 1]) | ||||
|         }, k = function (a) { | ||||
|             var b; | ||||
|             switch (a) { | ||||
|                 case"undefined": | ||||
|                     a = c; | ||||
|                     break; | ||||
|                 case"null": | ||||
|                     a = null; | ||||
|                     break; | ||||
|                 case"true": | ||||
|                     a = !0; | ||||
|                     break; | ||||
|                 case"false": | ||||
|                     a = !1; | ||||
|                     break; | ||||
|                 default: | ||||
|                     b = parseFloat(a), a == b && (a = b) | ||||
|             } | ||||
|             return a | ||||
|         }, l = function (a) { | ||||
|             var b, c = []; | ||||
|             for (b = a.length; b--;) c[b] = k(a[b]); | ||||
|             return c | ||||
|         }, m = function (a, b) { | ||||
|             var c, d, e = []; | ||||
|             for (c = 0, d = a.length; c < d; c++) a[c] !== b && e.push(a[c]); | ||||
|             return e | ||||
|         }, n = function (a) { | ||||
|             return !isNaN(parseFloat(a)) && isFinite(a) | ||||
|         }, s = function (a, b, c, e, f) { | ||||
|             var g, h; | ||||
|             a = (b === !1 ? parseFloat(a).toString() : a.toFixed(b)).split(""), g = (g = d.inArray(".", a)) < 0 ? a.length : g, g < a.length && (a[g] = f); | ||||
|             for (h = g - c; h > 0; h -= c) a.splice(h, 0, e); | ||||
|             return a.join("") | ||||
|         }, o = function (a, b, c) { | ||||
|             var d; | ||||
|             for (d = b.length; d--;) { | ||||
|                 if (c && b[d] === null) continue; | ||||
|                 if (b[d] !== a) return !1 | ||||
|             } | ||||
|             return !0 | ||||
|         }, p = function (a) { | ||||
|             var b = 0, c; | ||||
|             for (c = a.length; c--;) b += typeof a[c] == "number" ? a[c] : 0; | ||||
|             return b | ||||
|         }, r = function (a) { | ||||
|             return d.isArray(a) ? a : [a] | ||||
|         }, q = function (b) { | ||||
|             var c; | ||||
|             a.createStyleSheet ? a.createStyleSheet().cssText = b : (c = a.createElement("style"), c.type = "text/css", a.getElementsByTagName("head")[0].appendChild(c), c[typeof a.body.style.WebkitAppearance == "string" ? "innerText" : "innerHTML"] = b) | ||||
|         }, d.fn.simpledraw = function (b, e, f, g) { | ||||
|             var h, i; | ||||
|             if (f && (h = this.data("_jqs_vcanvas"))) return h; | ||||
|             if (d.fn.sparkline.canvas === !1) return !1; | ||||
|             if (d.fn.sparkline.canvas === c) { | ||||
|                 var j = a.createElement("canvas"); | ||||
|                 if (!j.getContext || !j.getContext("2d")) { | ||||
|                     if (!a.namespaces || !!a.namespaces.v) return d.fn.sparkline.canvas = !1, !1; | ||||
|                     a.namespaces.add("v", "urn:schemas-microsoft-com:vml", "#default#VML"), d.fn.sparkline.canvas = function (a, b, c, d) { | ||||
|                         return new J(a, b, c) | ||||
|                     } | ||||
|                 } else d.fn.sparkline.canvas = function (a, b, c, d) { | ||||
|                     return new I(a, b, c, d) | ||||
|                 } | ||||
|             } | ||||
|             return b === c && (b = d(this).innerWidth()), e === c && (e = d(this).innerHeight()), h = d.fn.sparkline.canvas(b, e, this, g), i = d(this).data("_jqs_mhandler"), i && i.registerCanvas(h), h | ||||
|         }, d.fn.cleardraw = function () { | ||||
|             var a = this.data("_jqs_vcanvas"); | ||||
|             a && a.reset() | ||||
|         }, d.RangeMapClass = t = g({ | ||||
|             init: function (a) { | ||||
|                 var b, c, d = []; | ||||
|                 for (b in a) a.hasOwnProperty(b) && typeof b == "string" && b.indexOf(":") > -1 && (c = b.split(":"), c[0] = c[0].length === 0 ? -Infinity : parseFloat(c[0]), c[1] = c[1].length === 0 ? Infinity : parseFloat(c[1]), c[2] = a[b], d.push(c)); | ||||
|                 this.map = a, this.rangelist = d || !1 | ||||
|             }, get: function (a) { | ||||
|                 var b = this.rangelist, d, e, f; | ||||
|                 if ((f = this.map[a]) !== c) return f; | ||||
|                 if (b) for (d = b.length; d--;) { | ||||
|                     e = b[d]; | ||||
|                     if (e[0] <= a && e[1] >= a) return e[2] | ||||
|                 } | ||||
|                 return c | ||||
|             } | ||||
|         }), d.range_map = function (a) { | ||||
|             return new t(a) | ||||
|         }, u = g({ | ||||
|             init: function (a, b) { | ||||
|                 var c = d(a); | ||||
|                 this.$el = c, this.options = b, this.currentPageX = 0, this.currentPageY = 0, this.el = a, this.splist = [], this.tooltip = null, this.over = !1, this.displayTooltips = !b.get("disableTooltips"), this.highlightEnabled = !b.get("disableHighlight") | ||||
|             }, registerSparkline: function (a) { | ||||
|                 this.splist.push(a), this.over && this.updateDisplay() | ||||
|             }, registerCanvas: function (a) { | ||||
|                 var b = d(a.canvas); | ||||
|                 this.canvas = a, this.$canvas = b, b.mouseenter(d.proxy(this.mouseenter, this)), b.mouseleave(d.proxy(this.mouseleave, this)), b.click(d.proxy(this.mouseclick, this)) | ||||
|             }, reset: function (a) { | ||||
|                 this.splist = [], this.tooltip && a && (this.tooltip.remove(), this.tooltip = c) | ||||
|             }, mouseclick: function (a) { | ||||
|                 var b = d.Event("sparklineClick"); | ||||
|                 b.originalEvent = a, b.sparklines = this.splist, this.$el.trigger(b) | ||||
|             }, mouseenter: function (b) { | ||||
|                 d(a.body).unbind("mousemove.jqs"), d(a.body).bind("mousemove.jqs", d.proxy(this.mousemove, this)), this.over = !0, this.currentPageX = b.pageX, this.currentPageY = b.pageY, this.currentEl = b.target, !this.tooltip && this.displayTooltips && (this.tooltip = new v(this.options), this.tooltip.updatePosition(b.pageX, b.pageY)), this.updateDisplay() | ||||
|             }, mouseleave: function () { | ||||
|                 d(a.body).unbind("mousemove.jqs"); | ||||
|                 var b = this.splist, c = b.length, e = !1, f, g; | ||||
|                 this.over = !1, this.currentEl = null, this.tooltip && (this.tooltip.remove(), this.tooltip = null); | ||||
|                 for (g = 0; g < c; g++) f = b[g], f.clearRegionHighlight() && (e = !0); | ||||
|                 e && this.canvas.render() | ||||
|             }, mousemove: function (a) { | ||||
|                 this.currentPageX = a.pageX, this.currentPageY = a.pageY, this.currentEl = a.target, this.tooltip && this.tooltip.updatePosition(a.pageX, a.pageY), this.updateDisplay() | ||||
|             }, updateDisplay: function () { | ||||
|                 var a = this.splist, b = a.length, c = !1, e = this.$canvas.offset(), f = this.currentPageX - e.left, | ||||
|                     g = this.currentPageY - e.top, h, i, j, k, l; | ||||
|                 if (!this.over) return; | ||||
|                 for (j = 0; j < b; j++) i = a[j], k = i.setRegionHighlight(this.currentEl, f, g), k && (c = !0); | ||||
|                 if (c) { | ||||
|                     l = d.Event("sparklineRegionChange"), l.sparklines = this.splist, this.$el.trigger(l); | ||||
|                     if (this.tooltip) { | ||||
|                         h = ""; | ||||
|                         for (j = 0; j < b; j++) i = a[j], h += i.getCurrentRegionTooltip(); | ||||
|                         this.tooltip.setContent(h) | ||||
|                     } | ||||
|                     this.disableHighlight || this.canvas.render() | ||||
|                 } | ||||
|                 k === null && this.mouseleave() | ||||
|             } | ||||
|         }), v = g({ | ||||
|             sizeStyle: "position: static !important;display: block !important;visibility: hidden !important;float: left !important;", | ||||
|             init: function (b) { | ||||
|                 var c = b.get("tooltipClassname", "jqstooltip"), e = this.sizeStyle, f; | ||||
|                 this.container = b.get("tooltipContainer") || a.body, this.tooltipOffsetX = b.get("tooltipOffsetX", 10), this.tooltipOffsetY = b.get("tooltipOffsetY", 12), d("#jqssizetip").remove(), d("#jqstooltip").remove(), this.sizetip = d("<div/>", { | ||||
|                     id: "jqssizetip", | ||||
|                     style: e, | ||||
|                     "class": c | ||||
|                 }), this.tooltip = d("<div/>", { | ||||
|                     id: "jqstooltip", | ||||
|                     "class": c | ||||
|                 }).appendTo(this.container), f = this.tooltip.offset(), this.offsetLeft = f.left, this.offsetTop = f.top, this.hidden = !0, d(window).unbind("resize.jqs scroll.jqs"), d(window).bind("resize.jqs scroll.jqs", d.proxy(this.updateWindowDims, this)), this.updateWindowDims() | ||||
|             }, | ||||
|             updateWindowDims: function () { | ||||
|                 this.scrollTop = d(window).scrollTop(), this.scrollLeft = d(window).scrollLeft(), this.scrollRight = this.scrollLeft + d(window).width(), this.updatePosition() | ||||
|             }, | ||||
|             getSize: function (a) { | ||||
|                 this.sizetip.html(a).appendTo(this.container), this.width = this.sizetip.width() + 1, this.height = this.sizetip.height(), this.sizetip.remove() | ||||
|             }, | ||||
|             setContent: function (a) { | ||||
|                 if (!a) { | ||||
|                     this.tooltip.css("visibility", "hidden"), this.hidden = !0; | ||||
|                     return | ||||
|                 } | ||||
|                 this.getSize(a), this.tooltip.html(a).css({ | ||||
|                     width: this.width, | ||||
|                     height: this.height, | ||||
|                     visibility: "visible" | ||||
|                 }), this.hidden && (this.hidden = !1, this.updatePosition()) | ||||
|             }, | ||||
|             updatePosition: function (a, b) { | ||||
|                 if (a === c) { | ||||
|                     if (this.mousex === c) return; | ||||
|                     a = this.mousex - this.offsetLeft, b = this.mousey - this.offsetTop | ||||
|                 } else this.mousex = a -= this.offsetLeft, this.mousey = b -= this.offsetTop; | ||||
|                 if (!this.height || !this.width || this.hidden) return; | ||||
|                 b -= this.height + this.tooltipOffsetY, a += this.tooltipOffsetX, b < this.scrollTop && (b = this.scrollTop), a < this.scrollLeft ? a = this.scrollLeft : a + this.width > this.scrollRight && (a = this.scrollRight - this.width), this.tooltip.css({ | ||||
|                     left: a, | ||||
|                     top: b | ||||
|                 }) | ||||
|             }, | ||||
|             remove: function () { | ||||
|                 this.tooltip.remove(), this.sizetip.remove(), this.sizetip = this.tooltip = c, d(window).unbind("resize.jqs scroll.jqs") | ||||
|             } | ||||
|         }), F = function () { | ||||
|             q(E) | ||||
|         }, d(F), K = [], d.fn.sparkline = function (b, e) { | ||||
|             return this.each(function () { | ||||
|                 var f = new d.fn.sparkline.options(this, e), g = d(this), h, i; | ||||
|                 h = function () { | ||||
|                     var e, h, i, j, k, l, m; | ||||
|                     if (b === "html" || b === c) { | ||||
|                         m = this.getAttribute(f.get("tagValuesAttribute")); | ||||
|                         if (m === c || m === null) m = g.html(); | ||||
|                         e = m.replace(/(^\s*<!--)|(-->\s*$)|\s+/g, "").split(",") | ||||
|                     } else e = b; | ||||
|                     h = f.get("width") === "auto" ? e.length * f.get("defaultPixelsPerValue") : f.get("width"); | ||||
|                     if (f.get("height") === "auto") { | ||||
|                         if (!f.get("composite") || !d.data(this, "_jqs_vcanvas")) j = a.createElement("span"), j.innerHTML = "a", g.html(j), i = d(j).innerHeight() || d(j).height(), d(j).remove(), j = null | ||||
|                     } else i = f.get("height"); | ||||
|                     f.get("disableInteraction") ? k = !1 : (k = d.data(this, "_jqs_mhandler"), k ? f.get("composite") || k.reset() : (k = new u(this, f), d.data(this, "_jqs_mhandler", k))); | ||||
|                     if (f.get("composite") && !d.data(this, "_jqs_vcanvas")) { | ||||
|                         d.data(this, "_jqs_errnotify") || (alert("Attempted to attach a composite sparkline to an element with no existing sparkline"), d.data(this, "_jqs_errnotify", !0)); | ||||
|                         return | ||||
|                     } | ||||
|                     l = new (d.fn.sparkline[f.get("type")])(this, e, f, h, i), l.render(), k && k.registerSparkline(l) | ||||
|                 }; | ||||
|                 if (d(this).html() && !f.get("disableHiddenCheck") && d(this).is(":hidden") || !d(this).parents("body").length) { | ||||
|                     if (!f.get("composite") && d.data(this, "_jqs_pending")) for (i = K.length; i; i--) K[i - 1][0] == this && K.splice(i - 1, 1); | ||||
|                     K.push([this, h]), d.data(this, "_jqs_pending", !0) | ||||
|                 } else h.call(this) | ||||
|             }) | ||||
|         }, d.fn.sparkline.defaults = f(), d.sparkline_display_visible = function () { | ||||
|             var a, b, c, e = []; | ||||
|             for (b = 0, c = K.length; b < c; b++) a = K[b][0], d(a).is(":visible") && !d(a).parents().is(":hidden") ? (K[b][1].call(a), d.data(K[b][0], "_jqs_pending", !1), e.push(b)) : !d(a).closest("html").length && !d.data(a, "_jqs_pending") && (d.data(K[b][0], "_jqs_pending", !1), e.push(b)); | ||||
|             for (b = e.length; b; b--) K.splice(e[b - 1], 1) | ||||
|         }, d.fn.sparkline.options = g({ | ||||
|             init: function (a, b) { | ||||
|                 var c, f, g, h; | ||||
|                 this.userOptions = b = b || {}, this.tag = a, this.tagValCache = {}, f = d.fn.sparkline.defaults, g = f.common, this.tagOptionsPrefix = b.enableTagOptions && (b.tagOptionsPrefix || g.tagOptionsPrefix), h = this.getTagSetting("type"), h === e ? c = f[b.type || g.type] : c = f[h], this.mergedOptions = d.extend({}, g, c, b) | ||||
|             }, getTagSetting: function (a) { | ||||
|                 var b = this.tagOptionsPrefix, d, f, g, h; | ||||
|                 if (b === !1 || b === c) return e; | ||||
|                 if (this.tagValCache.hasOwnProperty(a)) d = this.tagValCache.key; else { | ||||
|                     d = this.tag.getAttribute(b + a); | ||||
|                     if (d === c || d === null) d = e; else if (d.substr(0, 1) === "[") { | ||||
|                         d = d.substr(1, d.length - 2).split(","); | ||||
|                         for (f = d.length; f--;) d[f] = k(d[f].replace(/(^\s*)|(\s*$)/g, "")) | ||||
|                     } else if (d.substr(0, 1) === "{") { | ||||
|                         g = d.substr(1, d.length - 2).split(","), d = {}; | ||||
|                         for (f = g.length; f--;) h = g[f].split(":", 2), d[h[0].replace(/(^\s*)|(\s*$)/g, "")] = k(h[1].replace(/(^\s*)|(\s*$)/g, "")) | ||||
|                     } else d = k(d); | ||||
|                     this.tagValCache.key = d | ||||
|                 } | ||||
|                 return d | ||||
|             }, get: function (a, b) { | ||||
|                 var d = this.getTagSetting(a), f; | ||||
|                 return d !== e ? d : (f = this.mergedOptions[a]) === c ? b : f | ||||
|             } | ||||
|         }), d.fn.sparkline._base = g({ | ||||
|             disabled: !1, init: function (a, b, e, f, g) { | ||||
|                 this.el = a, this.$el = d(a), this.values = b, this.options = e, this.width = f, this.height = g, this.currentRegion = c | ||||
|             }, initTarget: function () { | ||||
|                 var a = !this.options.get("disableInteraction"); | ||||
|                 (this.target = this.$el.simpledraw(this.width, this.height, this.options.get("composite"), a)) ? (this.canvasWidth = this.target.pixelWidth, this.canvasHeight = this.target.pixelHeight) : this.disabled = !0 | ||||
|             }, render: function () { | ||||
|                 return this.disabled ? (this.el.innerHTML = "", !1) : !0 | ||||
|             }, getRegion: function (a, b) { | ||||
|             }, setRegionHighlight: function (a, b, d) { | ||||
|                 var e = this.currentRegion, f = !this.options.get("disableHighlight"), g; | ||||
|                 return b > this.canvasWidth || d > this.canvasHeight || b < 0 || d < 0 ? null : (g = this.getRegion(a, b, d), e !== g ? (e !== c && f && this.removeHighlight(), this.currentRegion = g, g !== c && f && this.renderHighlight(), !0) : !1) | ||||
|             }, clearRegionHighlight: function () { | ||||
|                 return this.currentRegion !== c ? (this.removeHighlight(), this.currentRegion = c, !0) : !1 | ||||
|             }, renderHighlight: function () { | ||||
|                 this.changeHighlight(!0) | ||||
|             }, removeHighlight: function () { | ||||
|                 this.changeHighlight(!1) | ||||
|             }, changeHighlight: function (a) { | ||||
|             }, getCurrentRegionTooltip: function () { | ||||
|                 var a = this.options, b = "", e = [], f, g, i, j, k, l, m, n, o, p, q, r, s, t; | ||||
|                 if (this.currentRegion === c) return ""; | ||||
|                 f = this.getCurrentRegionFields(), q = a.get("tooltipFormatter"); | ||||
|                 if (q) return q(this, a, f); | ||||
|                 a.get("tooltipChartTitle") && (b += '<div class="jqs jqstitle">' + a.get("tooltipChartTitle") + "</div>\n"), g = this.options.get("tooltipFormat"); | ||||
|                 if (!g) return ""; | ||||
|                 d.isArray(g) || (g = [g]), d.isArray(f) || (f = [f]), m = this.options.get("tooltipFormatFieldlist"), n = this.options.get("tooltipFormatFieldlistKey"); | ||||
|                 if (m && n) { | ||||
|                     o = []; | ||||
|                     for (l = f.length; l--;) p = f[l][n], (t = d.inArray(p, m)) != -1 && (o[t] = f[l]); | ||||
|                     f = o | ||||
|                 } | ||||
|                 i = g.length, s = f.length; | ||||
|                 for (l = 0; l < i; l++) { | ||||
|                     r = g[l], typeof r == "string" && (r = new h(r)), j = r.fclass || "jqsfield"; | ||||
|                     for (t = 0; t < s; t++) if (!f[t].isNull || !a.get("tooltipSkipNull")) d.extend(f[t], { | ||||
|                         prefix: a.get("tooltipPrefix"), | ||||
|                         suffix: a.get("tooltipSuffix") | ||||
|                     }), k = r.render(f[t], a.get("tooltipValueLookups"), a), e.push('<div class="' + j + '">' + k + "</div>") | ||||
|                 } | ||||
|                 return e.length ? b + e.join("\n") : "" | ||||
|             }, getCurrentRegionFields: function () { | ||||
|             }, calcHighlightColor: function (a, c) { | ||||
|                 var d = c.get("highlightColor"), e = c.get("highlightLighten"), f, g, h, j; | ||||
|                 if (d) return d; | ||||
|                 if (e) { | ||||
|                     f = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(a) || /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(a); | ||||
|                     if (f) { | ||||
|                         h = [], g = a.length === 4 ? 16 : 1; | ||||
|                         for (j = 0; j < 3; j++) h[j] = i(b.round(parseInt(f[j + 1], 16) * g * e), 0, 255); | ||||
|                         return "rgb(" + h.join(",") + ")" | ||||
|                     } | ||||
|                 } | ||||
|                 return a | ||||
|             } | ||||
|         }), w = { | ||||
|             changeHighlight: function (a) { | ||||
|                 var b = this.currentRegion, c = this.target, e = this.regionShapes[b], f; | ||||
|                 e && (f = this.renderRegion(b, a), d.isArray(f) || d.isArray(e) ? (c.replaceWithShapes(e, f), this.regionShapes[b] = d.map(f, function (a) { | ||||
|                     return a.id | ||||
|                 })) : (c.replaceWithShape(e, f), this.regionShapes[b] = f.id)) | ||||
|             }, render: function () { | ||||
|                 var a = this.values, b = this.target, c = this.regionShapes, e, f, g, h; | ||||
|                 if (!this.cls._super.render.call(this)) return; | ||||
|                 for (g = a.length; g--;) { | ||||
|                     e = this.renderRegion(g); | ||||
|                     if (e) if (d.isArray(e)) { | ||||
|                         f = []; | ||||
|                         for (h = e.length; h--;) e[h].append(), f.push(e[h].id); | ||||
|                         c[g] = f | ||||
|                     } else e.append(), c[g] = e.id; else c[g] = null | ||||
|                 } | ||||
|                 b.render() | ||||
|             } | ||||
|         }, d.fn.sparkline.line = x = g(d.fn.sparkline._base, { | ||||
|             type: "line", init: function (a, b, c, d, e) { | ||||
|                 x._super.init.call(this, a, b, c, d, e), this.vertices = [], this.regionMap = [], this.xvalues = [], this.yvalues = [], this.yminmax = [], this.hightlightSpotId = null, this.lastShapeId = null, this.initTarget() | ||||
|             }, getRegion: function (a, b, d) { | ||||
|                 var e, f = this.regionMap; | ||||
|                 for (e = f.length; e--;) if (f[e] !== null && b >= f[e][0] && b <= f[e][1]) return f[e][2]; | ||||
|                 return c | ||||
|             }, getCurrentRegionFields: function () { | ||||
|                 var a = this.currentRegion; | ||||
|                 return { | ||||
|                     isNull: this.yvalues[a] === null, | ||||
|                     x: this.xvalues[a], | ||||
|                     y: this.yvalues[a], | ||||
|                     color: this.options.get("lineColor"), | ||||
|                     fillColor: this.options.get("fillColor"), | ||||
|                     offset: a | ||||
|                 } | ||||
|             }, renderHighlight: function () { | ||||
|                 var a = this.currentRegion, b = this.target, d = this.vertices[a], e = this.options, | ||||
|                     f = e.get("spotRadius"), g = e.get("highlightSpotColor"), h = e.get("highlightLineColor"), i, j; | ||||
|                 if (!d) return; | ||||
|                 f && g && (i = b.drawCircle(d[0], d[1], f, c, g), this.highlightSpotId = i.id, b.insertAfterShape(this.lastShapeId, i)), h && (j = b.drawLine(d[0], this.canvasTop, d[0], this.canvasTop + this.canvasHeight, h), this.highlightLineId = j.id, b.insertAfterShape(this.lastShapeId, j)) | ||||
|             }, removeHighlight: function () { | ||||
|                 var a = this.target; | ||||
|                 this.highlightSpotId && (a.removeShapeId(this.highlightSpotId), this.highlightSpotId = null), this.highlightLineId && (a.removeShapeId(this.highlightLineId), this.highlightLineId = null) | ||||
|             }, scanValues: function () { | ||||
|                 var a = this.values, c = a.length, d = this.xvalues, e = this.yvalues, f = this.yminmax, g, h, i, j, k; | ||||
|                 for (g = 0; g < c; g++) h = a[g], i = typeof a[g] == "string", j = typeof a[g] == "object" && a[g] instanceof Array, k = i && a[g].split(":"), i && k.length === 2 ? (d.push(Number(k[0])), e.push(Number(k[1])), f.push(Number(k[1]))) : j ? (d.push(h[0]), e.push(h[1]), f.push(h[1])) : (d.push(g), a[g] === null || a[g] === "null" ? e.push(null) : (e.push(Number(h)), f.push(Number(h)))); | ||||
|                 this.options.get("xvalues") && (d = this.options.get("xvalues")), this.maxy = this.maxyorg = b.max.apply(b, f), this.miny = this.minyorg = b.min.apply(b, f), this.maxx = b.max.apply(b, d), this.minx = b.min.apply(b, d), this.xvalues = d, this.yvalues = e, this.yminmax = f | ||||
|             }, processRangeOptions: function () { | ||||
|                 var a = this.options, b = a.get("normalRangeMin"), d = a.get("normalRangeMax"); | ||||
|                 b !== c && (b < this.miny && (this.miny = b), d > this.maxy && (this.maxy = d)), a.get("chartRangeMin") !== c && (a.get("chartRangeClip") || a.get("chartRangeMin") < this.miny) && (this.miny = a.get("chartRangeMin")), a.get("chartRangeMax") !== c && (a.get("chartRangeClip") || a.get("chartRangeMax") > this.maxy) && (this.maxy = a.get("chartRangeMax")), a.get("chartRangeMinX") !== c && (a.get("chartRangeClipX") || a.get("chartRangeMinX") < this.minx) && (this.minx = a.get("chartRangeMinX")), a.get("chartRangeMaxX") !== c && (a.get("chartRangeClipX") || a.get("chartRangeMaxX") > this.maxx) && (this.maxx = a.get("chartRangeMaxX")) | ||||
|             }, drawNormalRange: function (a, d, e, f, g) { | ||||
|                 var h = this.options.get("normalRangeMin"), i = this.options.get("normalRangeMax"), | ||||
|                     j = d + b.round(e - e * ((i - this.miny) / g)), k = b.round(e * (i - h) / g); | ||||
|                 this.target.drawRect(a, j, f, k, c, this.options.get("normalRangeColor")).append() | ||||
|             }, render: function () { | ||||
|                 var a = this.options, e = this.target, f = this.canvasWidth, g = this.canvasHeight, h = this.vertices, | ||||
|                     i = a.get("spotRadius"), j = this.regionMap, k, l, m, n, o, p, q, r, s, u, v, w, y, z, A, B, C, D, | ||||
|                     E, F, G, H, I, J, K; | ||||
|                 if (!x._super.render.call(this)) return; | ||||
|                 this.scanValues(), this.processRangeOptions(), I = this.xvalues, J = this.yvalues; | ||||
|                 if (!this.yminmax.length || this.yvalues.length < 2) return; | ||||
|                 n = o = 0, k = this.maxx - this.minx === 0 ? 1 : this.maxx - this.minx, l = this.maxy - this.miny === 0 ? 1 : this.maxy - this.miny, m = this.yvalues.length - 1, i && (f < i * 4 || g < i * 4) && (i = 0); | ||||
|                 if (i) { | ||||
|                     G = a.get("highlightSpotColor") && !a.get("disableInteraction"); | ||||
|                     if (G || a.get("minSpotColor") || a.get("spotColor") && J[m] === this.miny) g -= b.ceil(i); | ||||
|                     if (G || a.get("maxSpotColor") || a.get("spotColor") && J[m] === this.maxy) g -= b.ceil(i), n += b.ceil(i); | ||||
|                     if (G || (a.get("minSpotColor") || a.get("maxSpotColor")) && (J[0] === this.miny || J[0] === this.maxy)) o += b.ceil(i), f -= b.ceil(i); | ||||
|                     if (G || a.get("spotColor") || a.get("minSpotColor") || a.get("maxSpotColor") && (J[m] === this.miny || J[m] === this.maxy)) f -= b.ceil(i) | ||||
|                 } | ||||
|                 g--, a.get("normalRangeMin") !== c && !a.get("drawNormalOnTop") && this.drawNormalRange(o, n, g, f, l), q = [], r = [q], z = A = null, B = J.length; | ||||
|                 for (K = 0; K < B; K++) s = I[K], v = I[K + 1], u = J[K], w = o + b.round((s - this.minx) * (f / k)), y = K < B - 1 ? o + b.round((v - this.minx) * (f / k)) : f, A = w + (y - w) / 2, j[K] = [z || 0, A, K], z = A, u === null ? K && (J[K - 1] !== null && (q = [], r.push(q)), h.push(null)) : (u < this.miny && (u = this.miny), u > this.maxy && (u = this.maxy), q.length || q.push([w, n + g]), p = [w, n + b.round(g - g * ((u - this.miny) / l))], q.push(p), h.push(p)); | ||||
|                 C = [], D = [], E = r.length; | ||||
|                 for (K = 0; K < E; K++) q = r[K], q.length && (a.get("fillColor") && (q.push([q[q.length - 1][0], n + g]), D.push(q.slice(0)), q.pop()), q.length > 2 && (q[0] = [q[0][0], q[1][1]]), C.push(q)); | ||||
|                 E = D.length; | ||||
|                 for (K = 0; K < E; K++) e.drawShape(D[K], a.get("fillColor"), a.get("fillColor")).append(); | ||||
|                 a.get("normalRangeMin") !== c && a.get("drawNormalOnTop") && this.drawNormalRange(o, n, g, f, l), E = C.length; | ||||
|                 for (K = 0; K < E; K++) e.drawShape(C[K], a.get("lineColor"), c, a.get("lineWidth")).append(); | ||||
|                 if (i && a.get("valueSpots")) { | ||||
|                     F = a.get("valueSpots"), F.get === c && (F = new t(F)); | ||||
|                     for (K = 0; K < B; K++) H = F.get(J[K]), H && e.drawCircle(o + b.round((I[K] - this.minx) * (f / k)), n + b.round(g - g * ((J[K] - this.miny) / l)), i, c, H).append() | ||||
|                 } | ||||
|                 i && a.get("spotColor") && J[m] !== null && e.drawCircle(o + b.round((I[I.length - 1] - this.minx) * (f / k)), n + b.round(g - g * ((J[m] - this.miny) / l)), i, c, a.get("spotColor")).append(), this.maxy !== this.minyorg && (i && a.get("minSpotColor") && (s = I[d.inArray(this.minyorg, J)], e.drawCircle(o + b.round((s - this.minx) * (f / k)), n + b.round(g - g * ((this.minyorg - this.miny) / l)), i, c, a.get("minSpotColor")).append()), i && a.get("maxSpotColor") && (s = I[d.inArray(this.maxyorg, J)], e.drawCircle(o + b.round((s - this.minx) * (f / k)), n + b.round(g - g * ((this.maxyorg - this.miny) / l)), i, c, a.get("maxSpotColor")).append())), this.lastShapeId = e.getLastShapeId(), this.canvasTop = n, e.render() | ||||
|             } | ||||
|         }), d.fn.sparkline.bar = y = g(d.fn.sparkline._base, w, { | ||||
|             type: "bar", init: function (a, e, f, g, h) { | ||||
|                 var j = parseInt(f.get("barWidth"), 10), n = parseInt(f.get("barSpacing"), 10), | ||||
|                     o = f.get("chartRangeMin"), p = f.get("chartRangeMax"), q = f.get("chartRangeClip"), r = Infinity, | ||||
|                     s = -Infinity, u, v, w, x, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R; | ||||
|                 y._super.init.call(this, a, e, f, g, h); | ||||
|                 for (A = 0, B = e.length; A < B; A++) { | ||||
|                     O = e[A], u = typeof O == "string" && O.indexOf(":") > -1; | ||||
|                     if (u || d.isArray(O)) J = !0, u && (O = e[A] = l(O.split(":"))), O = m(O, null), v = b.min.apply(b, O), w = b.max.apply(b, O), v < r && (r = v), w > s && (s = w) | ||||
|                 } | ||||
|                 this.stacked = J, this.regionShapes = {}, this.barWidth = j, this.barSpacing = n, this.totalBarWidth = j + n, this.width = g = e.length * j + (e.length - 1) * n, this.initTarget(), q && (H = o === c ? -Infinity : o, I = p === c ? Infinity : p), z = [], x = J ? [] : z; | ||||
|                 var S = [], T = []; | ||||
|                 for (A = 0, B = e.length; A < B; A++) if (J) { | ||||
|                     K = e[A], e[A] = N = [], S[A] = 0, x[A] = T[A] = 0; | ||||
|                     for (L = 0, M = K.length; L < M; L++) O = N[L] = q ? i(K[L], H, I) : K[L], O !== null && (O > 0 && (S[A] += O), r < 0 && s > 0 ? O < 0 ? T[A] += b.abs(O) : x[A] += O : x[A] += b.abs(O - (O < 0 ? s : r)), z.push(O)) | ||||
|                 } else O = q ? i(e[A], H, I) : e[A], O = e[A] = k(O), O !== null && z.push(O); | ||||
|                 this.max = G = b.max.apply(b, z), this.min = F = b.min.apply(b, z), this.stackMax = s = J ? b.max.apply(b, S) : G, this.stackMin = r = J ? b.min.apply(b, z) : F, f.get("chartRangeMin") !== c && (f.get("chartRangeClip") || f.get("chartRangeMin") < F) && (F = f.get("chartRangeMin")), f.get("chartRangeMax") !== c && (f.get("chartRangeClip") || f.get("chartRangeMax") > G) && (G = f.get("chartRangeMax")), this.zeroAxis = D = f.get("zeroAxis", !0), F <= 0 && G >= 0 && D ? E = 0 : D == 0 ? E = F : F > 0 ? E = F : E = G, this.xaxisOffset = E, C = J ? b.max.apply(b, x) + b.max.apply(b, T) : G - F, this.canvasHeightEf = D && F < 0 ? this.canvasHeight - 2 : this.canvasHeight - 1, F < E ? (Q = J && G >= 0 ? s : G, P = (Q - E) / C * this.canvasHeight, P !== b.ceil(P) && (this.canvasHeightEf -= 2, P = b.ceil(P))) : P = this.canvasHeight, this.yoffset = P, d.isArray(f.get("colorMap")) ? (this.colorMapByIndex = f.get("colorMap"), this.colorMapByValue = null) : (this.colorMapByIndex = null, this.colorMapByValue = f.get("colorMap"), this.colorMapByValue && this.colorMapByValue.get === c && (this.colorMapByValue = new t(this.colorMapByValue))), this.range = C | ||||
|             }, getRegion: function (a, d, e) { | ||||
|                 var f = b.floor(d / this.totalBarWidth); | ||||
|                 return f < 0 || f >= this.values.length ? c : f | ||||
|             }, getCurrentRegionFields: function () { | ||||
|                 var a = this.currentRegion, b = r(this.values[a]), c = [], d, e; | ||||
|                 for (e = b.length; e--;) d = b[e], c.push({ | ||||
|                     isNull: d === null, | ||||
|                     value: d, | ||||
|                     color: this.calcColor(e, d, a), | ||||
|                     offset: a | ||||
|                 }); | ||||
|                 return c | ||||
|             }, calcColor: function (a, b, e) { | ||||
|                 var f = this.colorMapByIndex, g = this.colorMapByValue, h = this.options, i, j; | ||||
|                 return this.stacked ? i = h.get("stackedBarColor") : i = b < 0 ? h.get("negBarColor") : h.get("barColor"), b === 0 && h.get("zeroColor") !== c && (i = h.get("zeroColor")), g && (j = g.get(b)) ? i = j : f && f.length > e && (i = f[e]), d.isArray(i) ? i[a % i.length] : i | ||||
|             }, renderRegion: function (a, e) { | ||||
|                 var f = this.values[a], g = this.options, h = this.xaxisOffset, i = [], j = this.range, | ||||
|                     k = this.stacked, l = this.target, m = a * this.totalBarWidth, n = this.canvasHeightEf, | ||||
|                     p = this.yoffset, q, r, s, t, u, v, w, x, y, z; | ||||
|                 f = d.isArray(f) ? f : [f], w = f.length, x = f[0], t = o(null, f), z = o(h, f, !0); | ||||
|                 if (t) return g.get("nullColor") ? (s = e ? g.get("nullColor") : this.calcHighlightColor(g.get("nullColor"), g), q = p > 0 ? p - 1 : p, l.drawRect(m, q, this.barWidth - 1, 0, s, s)) : c; | ||||
|                 u = p; | ||||
|                 for (v = 0; v < w; v++) { | ||||
|                     x = f[v]; | ||||
|                     if (k && x === h) { | ||||
|                         if (!z || y) continue; | ||||
|                         y = !0 | ||||
|                     } | ||||
|                     j > 0 ? r = b.floor(n * (b.abs(x - h) / j)) + 1 : r = 1, x < h || x === h && p === 0 ? (q = u, u += r) : (q = p - r, p -= r), s = this.calcColor(v, x, a), e && (s = this.calcHighlightColor(s, g)), i.push(l.drawRect(m, q, this.barWidth - 1, r - 1, s, s)) | ||||
|                 } | ||||
|                 return i.length === 1 ? i[0] : i | ||||
|             } | ||||
|         }), d.fn.sparkline.tristate = z = g(d.fn.sparkline._base, w, { | ||||
|             type: "tristate", init: function (a, b, e, f, g) { | ||||
|                 var h = parseInt(e.get("barWidth"), 10), i = parseInt(e.get("barSpacing"), 10); | ||||
|                 z._super.init.call(this, a, b, e, f, g), this.regionShapes = {}, this.barWidth = h, this.barSpacing = i, this.totalBarWidth = h + i, this.values = d.map(b, Number), this.width = f = b.length * h + (b.length - 1) * i, d.isArray(e.get("colorMap")) ? (this.colorMapByIndex = e.get("colorMap"), this.colorMapByValue = null) : (this.colorMapByIndex = null, this.colorMapByValue = e.get("colorMap"), this.colorMapByValue && this.colorMapByValue.get === c && (this.colorMapByValue = new t(this.colorMapByValue))), this.initTarget() | ||||
|             }, getRegion: function (a, c, d) { | ||||
|                 return b.floor(c / this.totalBarWidth) | ||||
|             }, getCurrentRegionFields: function () { | ||||
|                 var a = this.currentRegion; | ||||
|                 return { | ||||
|                     isNull: this.values[a] === c, | ||||
|                     value: this.values[a], | ||||
|                     color: this.calcColor(this.values[a], a), | ||||
|                     offset: a | ||||
|                 } | ||||
|             }, calcColor: function (a, b) { | ||||
|                 var c = this.values, d = this.options, e = this.colorMapByIndex, f = this.colorMapByValue, g, h; | ||||
|                 return f && (h = f.get(a)) ? g = h : e && e.length > b ? g = e[b] : c[b] < 0 ? g = d.get("negBarColor") : c[b] > 0 ? g = d.get("posBarColor") : g = d.get("zeroBarColor"), g | ||||
|             }, renderRegion: function (a, c) { | ||||
|                 var d = this.values, e = this.options, f = this.target, g, h, i, j, k, l; | ||||
|                 g = f.pixelHeight, i = b.round(g / 2), j = a * this.totalBarWidth, d[a] < 0 ? (k = i, h = i - 1) : d[a] > 0 ? (k = 0, h = i - 1) : (k = i - 1, h = 2), l = this.calcColor(d[a], a); | ||||
|                 if (l === null) return; | ||||
|                 return c && (l = this.calcHighlightColor(l, e)), f.drawRect(j, k, this.barWidth - 1, h - 1, l, l) | ||||
|             } | ||||
|         }), d.fn.sparkline.discrete = A = g(d.fn.sparkline._base, w, { | ||||
|             type: "discrete", init: function (a, e, f, g, h) { | ||||
|                 A._super.init.call(this, a, e, f, g, h), this.regionShapes = {}, this.values = e = d.map(e, Number), this.min = b.min.apply(b, e), this.max = b.max.apply(b, e), this.range = this.max - this.min, this.width = g = f.get("width") === "auto" ? e.length * 2 : this.width, this.interval = b.floor(g / e.length), this.itemWidth = g / e.length, f.get("chartRangeMin") !== c && (f.get("chartRangeClip") || f.get("chartRangeMin") < this.min) && (this.min = f.get("chartRangeMin")), f.get("chartRangeMax") !== c && (f.get("chartRangeClip") || f.get("chartRangeMax") > this.max) && (this.max = f.get("chartRangeMax")), this.initTarget(), this.target && (this.lineHeight = f.get("lineHeight") === "auto" ? b.round(this.canvasHeight * .3) : f.get("lineHeight")) | ||||
|             }, getRegion: function (a, c, d) { | ||||
|                 return b.floor(c / this.itemWidth) | ||||
|             }, getCurrentRegionFields: function () { | ||||
|                 var a = this.currentRegion; | ||||
|                 return {isNull: this.values[a] === c, value: this.values[a], offset: a} | ||||
|             }, renderRegion: function (a, c) { | ||||
|                 var d = this.values, e = this.options, f = this.min, g = this.max, h = this.range, j = this.interval, | ||||
|                     k = this.target, l = this.canvasHeight, m = this.lineHeight, n = l - m, o, p, q, r; | ||||
|                 return p = i(d[a], f, g), r = a * j, o = b.round(n - n * ((p - f) / h)), q = e.get("thresholdColor") && p < e.get("thresholdValue") ? e.get("thresholdColor") : e.get("lineColor"), c && (q = this.calcHighlightColor(q, e)), k.drawLine(r, o, r, o + m, q) | ||||
|             } | ||||
|         }), d.fn.sparkline.bullet = B = g(d.fn.sparkline._base, { | ||||
|             type: "bullet", init: function (a, d, e, f, g) { | ||||
|                 var h, i, j; | ||||
|                 B._super.init.call(this, a, d, e, f, g), this.values = d = l(d), j = d.slice(), j[0] = j[0] === null ? j[2] : j[0], j[1] = d[1] === null ? j[2] : j[1], h = b.min.apply(b, d), i = b.max.apply(b, d), e.get("base") === c ? h = h < 0 ? h : 0 : h = e.get("base"), this.min = h, this.max = i, this.range = i - h, this.shapes = {}, this.valueShapes = {}, this.regiondata = {}, this.width = f = e.get("width") === "auto" ? "4.0em" : f, this.target = this.$el.simpledraw(f, g, e.get("composite")), d.length || (this.disabled = !0), this.initTarget() | ||||
|             }, getRegion: function (a, b, d) { | ||||
|                 var e = this.target.getShapeAt(a, b, d); | ||||
|                 return e !== c && this.shapes[e] !== c ? this.shapes[e] : c | ||||
|             }, getCurrentRegionFields: function () { | ||||
|                 var a = this.currentRegion; | ||||
|                 return {fieldkey: a.substr(0, 1), value: this.values[a.substr(1)], region: a} | ||||
|             }, changeHighlight: function (a) { | ||||
|                 var b = this.currentRegion, c = this.valueShapes[b], d; | ||||
|                 delete this.shapes[c]; | ||||
|                 switch (b.substr(0, 1)) { | ||||
|                     case"r": | ||||
|                         d = this.renderRange(b.substr(1), a); | ||||
|                         break; | ||||
|                     case"p": | ||||
|                         d = this.renderPerformance(a); | ||||
|                         break; | ||||
|                     case"t": | ||||
|                         d = this.renderTarget(a) | ||||
|                 } | ||||
|                 this.valueShapes[b] = d.id, this.shapes[d.id] = b, this.target.replaceWithShape(c, d) | ||||
|             }, renderRange: function (a, c) { | ||||
|                 var d = this.values[a], e = b.round(this.canvasWidth * ((d - this.min) / this.range)), | ||||
|                     f = this.options.get("rangeColors")[a - 2]; | ||||
|                 return c && (f = this.calcHighlightColor(f, this.options)), this.target.drawRect(0, 0, e - 1, this.canvasHeight - 1, f, f) | ||||
|             }, renderPerformance: function (a) { | ||||
|                 var c = this.values[1], d = b.round(this.canvasWidth * ((c - this.min) / this.range)), | ||||
|                     e = this.options.get("performanceColor"); | ||||
|                 return a && (e = this.calcHighlightColor(e, this.options)), this.target.drawRect(0, b.round(this.canvasHeight * .3), d - 1, b.round(this.canvasHeight * .4) - 1, e, e) | ||||
|             }, renderTarget: function (a) { | ||||
|                 var c = this.values[0], | ||||
|                     d = b.round(this.canvasWidth * ((c - this.min) / this.range) - this.options.get("targetWidth") / 2), | ||||
|                     e = b.round(this.canvasHeight * .1), f = this.canvasHeight - e * 2, | ||||
|                     g = this.options.get("targetColor"); | ||||
|                 return a && (g = this.calcHighlightColor(g, this.options)), this.target.drawRect(d, e, this.options.get("targetWidth") - 1, f - 1, g, g) | ||||
|             }, render: function () { | ||||
|                 var a = this.values.length, b = this.target, c, d; | ||||
|                 if (!B._super.render.call(this)) return; | ||||
|                 for (c = 2; c < a; c++) d = this.renderRange(c).append(), this.shapes[d.id] = "r" + c, this.valueShapes["r" + c] = d.id; | ||||
|                 this.values[1] !== null && (d = this.renderPerformance().append(), this.shapes[d.id] = "p1", this.valueShapes.p1 = d.id), this.values[0] !== null && (d = this.renderTarget().append(), this.shapes[d.id] = "t0", this.valueShapes.t0 = d.id), b.render() | ||||
|             } | ||||
|         }), d.fn.sparkline.pie = C = g(d.fn.sparkline._base, { | ||||
|             type: "pie", init: function (a, c, e, f, g) { | ||||
|                 var h = 0, i; | ||||
|                 C._super.init.call(this, a, c, e, f, g), this.shapes = {}, this.valueShapes = {}, this.values = c = d.map(c, Number), e.get("width") === "auto" && (this.width = this.height); | ||||
|                 if (c.length > 0) for (i = c.length; i--;) h += c[i]; | ||||
|                 this.total = h, this.initTarget(), this.radius = b.floor(b.min(this.canvasWidth, this.canvasHeight) / 2) | ||||
|             }, getRegion: function (a, b, d) { | ||||
|                 var e = this.target.getShapeAt(a, b, d); | ||||
|                 return e !== c && this.shapes[e] !== c ? this.shapes[e] : c | ||||
|             }, getCurrentRegionFields: function () { | ||||
|                 var a = this.currentRegion; | ||||
|                 return { | ||||
|                     isNull: this.values[a] === c, | ||||
|                     value: this.values[a], | ||||
|                     percent: this.values[a] / this.total * 100, | ||||
|                     color: this.options.get("sliceColors")[a % this.options.get("sliceColors").length], | ||||
|                     offset: a | ||||
|                 } | ||||
|             }, changeHighlight: function (a) { | ||||
|                 var b = this.currentRegion, c = this.renderSlice(b, a), d = this.valueShapes[b]; | ||||
|                 delete this.shapes[d], this.target.replaceWithShape(d, c), this.valueShapes[b] = c.id, this.shapes[c.id] = b | ||||
|             }, renderSlice: function (a, d) { | ||||
|                 var e = this.target, f = this.options, g = this.radius, h = f.get("borderWidth"), i = f.get("offset"), | ||||
|                     j = 2 * b.PI, k = this.values, l = this.total, m = i ? 2 * b.PI * (i / 360) : 0, n, o, p, q, r; | ||||
|                 q = k.length; | ||||
|                 for (p = 0; p < q; p++) { | ||||
|                     n = m, o = m, l > 0 && (o = m + j * (k[p] / l)); | ||||
|                     if (a === p) return r = f.get("sliceColors")[p % f.get("sliceColors").length], d && (r = this.calcHighlightColor(r, f)), e.drawPieSlice(g, g, g - h, n, o, c, r); | ||||
|                     m = o | ||||
|                 } | ||||
|             }, render: function () { | ||||
|                 var a = this.target, d = this.values, e = this.options, f = this.radius, g = e.get("borderWidth"), h, i; | ||||
|                 if (!C._super.render.call(this)) return; | ||||
|                 g && a.drawCircle(f, f, b.floor(f - g / 2), e.get("borderColor"), c, g).append(); | ||||
|                 for (i = d.length; i--;) d[i] && (h = this.renderSlice(i).append(), this.valueShapes[i] = h.id, this.shapes[h.id] = i); | ||||
|                 a.render() | ||||
|             } | ||||
|         }), d.fn.sparkline.box = D = g(d.fn.sparkline._base, { | ||||
|             type: "box", init: function (a, b, c, e, f) { | ||||
|                 D._super.init.call(this, a, b, c, e, f), this.values = d.map(b, Number), this.width = c.get("width") === "auto" ? "4.0em" : e, this.initTarget(), this.values.length || (this.disabled = 1) | ||||
|             }, getRegion: function () { | ||||
|                 return 1 | ||||
|             }, getCurrentRegionFields: function () { | ||||
|                 var a = [{field: "lq", value: this.quartiles[0]}, { | ||||
|                     field: "med", value: this.quartiles | ||||
|                         [1] | ||||
|                 }, {field: "uq", value: this.quartiles[2]}]; | ||||
|                 return this.loutlier !== c && a.push({ | ||||
|                     field: "lo", | ||||
|                     value: this.loutlier | ||||
|                 }), this.routlier !== c && a.push({ | ||||
|                     field: "ro", | ||||
|                     value: this.routlier | ||||
|                 }), this.lwhisker !== c && a.push({ | ||||
|                     field: "lw", | ||||
|                     value: this.lwhisker | ||||
|                 }), this.rwhisker !== c && a.push({field: "rw", value: this.rwhisker}), a | ||||
|             }, render: function () { | ||||
|                 var a = this.target, d = this.values, e = d.length, f = this.options, g = this.canvasWidth, | ||||
|                     h = this.canvasHeight, | ||||
|                     i = f.get("chartRangeMin") === c ? b.min.apply(b, d) : f.get("chartRangeMin"), | ||||
|                     k = f.get("chartRangeMax") === c ? b.max.apply(b, d) : f.get("chartRangeMax"), l = 0, m, n, o, p, q, | ||||
|                     r, s, t, u, v, w; | ||||
|                 if (!D._super.render.call(this)) return; | ||||
|                 if (f.get("raw")) f.get("showOutliers") && d.length > 5 ? (n = d[0], m = d[1], p = d[2], q = d[3], r = d[4], s = d[5], t = d[6]) : (m = d[0], p = d[1], q = d[2], r = d[3], s = d[4]); else { | ||||
|                     d.sort(function (a, b) { | ||||
|                         return a - b | ||||
|                     }), p = j(d, 1), q = j(d, 2), r = j(d, 3), o = r - p; | ||||
|                     if (f.get("showOutliers")) { | ||||
|                         m = s = c; | ||||
|                         for (u = 0; u < e; u++) m === c && d[u] > p - o * f.get("outlierIQR") && (m = d[u]), d[u] < r + o * f.get("outlierIQR") && (s = d[u]); | ||||
|                         n = d[0], t = d[e - 1] | ||||
|                     } else m = d[0], s = d[e - 1] | ||||
|                 } | ||||
|                 this.quartiles = [p, q, r], this.lwhisker = m, this.rwhisker = s, this.loutlier = n, this.routlier = t, w = g / (k - i + 1), f.get("showOutliers") && (l = b.ceil(f.get("spotRadius")), g -= 2 * b.ceil(f.get("spotRadius")), w = g / (k - i + 1), n < m && a.drawCircle((n - i) * w + l, h / 2, f.get("spotRadius"), f.get("outlierLineColor"), f.get("outlierFillColor")).append(), t > s && a.drawCircle((t - i) * w + l, h / 2, f.get("spotRadius"), f.get("outlierLineColor"), f.get("outlierFillColor")).append()), a.drawRect(b.round((p - i) * w + l), b.round(h * .1), b.round((r - p) * w), b.round(h * .8), f.get("boxLineColor"), f.get("boxFillColor")).append(), a.drawLine(b.round((m - i) * w + l), b.round(h / 2), b.round((p - i) * w + l), b.round(h / 2), f.get("lineColor")).append(), a.drawLine(b.round((m - i) * w + l), b.round(h / 4), b.round((m - i) * w + l), b.round(h - h / 4), f.get("whiskerColor")).append(), a.drawLine(b.round((s - i) * w + l), b.round(h / 2), b.round((r - i) * w + l), b.round(h / 2), f.get("lineColor")).append(), a.drawLine(b.round((s - i) * w + l), b.round(h / 4), b.round((s - i) * w + l), b.round(h - h / 4), f.get("whiskerColor")).append(), a.drawLine(b.round((q - i) * w + l), b.round(h * .1), b.round((q - i) * w + l), b.round(h * .9), f.get("medianColor")).append(), f.get("target") && (v = b.ceil(f.get("spotRadius")), a.drawLine(b.round((f.get("target") - i) * w + l), b.round(h / 2 - v), b.round((f.get("target") - i) * w + l), b.round(h / 2 + v), f.get("targetColor")).append(), a.drawLine(b.round((f.get("target") - i) * w + l - v), b.round(h / 2), b.round((f.get("target") - i) * w + l + v), b.round(h / 2), f.get("targetColor")).append()), a.render() | ||||
|             } | ||||
|         }), G = g({ | ||||
|             init: function (a, b, c, d) { | ||||
|                 this.target = a, this.id = b, this.type = c, this.args = d | ||||
|             }, append: function () { | ||||
|                 return this.target.appendShape(this), this | ||||
|             } | ||||
|         }), H = g({ | ||||
|             _pxregex: /(\d+)(px)?\s*$/i, init: function (a, b, c) { | ||||
|                 if (!a) return; | ||||
|                 this.width = a, this.height = b, this.target = c, this.lastShapeId = null, c[0] && (c = c[0]), d.data(c, "_jqs_vcanvas", this) | ||||
|             }, drawLine: function (a, b, c, d, e, f) { | ||||
|                 return this.drawShape([[a, b], [c, d]], e, f) | ||||
|             }, drawShape: function (a, b, c, d) { | ||||
|                 return this._genShape("Shape", [a, b, c, d]) | ||||
|             }, drawCircle: function (a, b, c, d, e, f) { | ||||
|                 return this._genShape("Circle", [a, b, c, d, e, f]) | ||||
|             }, drawPieSlice: function (a, b, c, d, e, f, g) { | ||||
|                 return this._genShape("PieSlice", [a, b, c, d, e, f, g]) | ||||
|             }, drawRect: function (a, b, c, d, e, f) { | ||||
|                 return this._genShape("Rect", [a, b, c, d, e, f]) | ||||
|             }, getElement: function () { | ||||
|                 return this.canvas | ||||
|             }, getLastShapeId: function () { | ||||
|                 return this.lastShapeId | ||||
|             }, reset: function () { | ||||
|                 alert("reset not implemented") | ||||
|             }, _insert: function (a, b) { | ||||
|                 d(b).html(a) | ||||
|             }, _calculatePixelDims: function (a, b, c) { | ||||
|                 var e; | ||||
|                 e = this._pxregex.exec(b), e ? this.pixelHeight = e[1] : this.pixelHeight = d(c).height(), e = this._pxregex.exec(a), e ? this.pixelWidth = e[1] : this.pixelWidth = d(c).width() | ||||
|             }, _genShape: function (a, b) { | ||||
|                 var c = L++; | ||||
|                 return b.unshift(c), new G(this, c, a, b) | ||||
|             }, appendShape: function (a) { | ||||
|                 alert("appendShape not implemented") | ||||
|             }, replaceWithShape: function (a, b) { | ||||
|                 alert("replaceWithShape not implemented") | ||||
|             }, insertAfterShape: function (a, b) { | ||||
|                 alert("insertAfterShape not implemented") | ||||
|             }, removeShapeId: function (a) { | ||||
|                 alert("removeShapeId not implemented") | ||||
|             }, getShapeAt: function (a, b, c) { | ||||
|                 alert("getShapeAt not implemented") | ||||
|             }, render: function () { | ||||
|                 alert("render not implemented") | ||||
|             } | ||||
|         }), I = g(H, { | ||||
|             init: function (b, e, f, g) { | ||||
|                 I._super.init.call(this, b, e, f), this.canvas = a.createElement("canvas"), f[0] && (f = f[0]), d.data(f, "_jqs_vcanvas", this), d(this.canvas).css({ | ||||
|                     display: "inline-block", | ||||
|                     width: b, | ||||
|                     height: e, | ||||
|                     verticalAlign: "top" | ||||
|                 }), this._insert(this.canvas, f), this._calculatePixelDims(b, e, this.canvas), this.canvas.width = this.pixelWidth, this.canvas.height = this.pixelHeight, this.interact = g, this.shapes = {}, this.shapeseq = [], this.currentTargetShapeId = c, d(this.canvas).css({ | ||||
|                     width: this.pixelWidth, | ||||
|                     height: this.pixelHeight | ||||
|                 }) | ||||
|             }, _getContext: function (a, b, d) { | ||||
|                 var e = this.canvas.getContext("2d"); | ||||
|                 return a !== c && (e.strokeStyle = a), e.lineWidth = d === c ? 1 : d, b !== c && (e.fillStyle = b), e | ||||
|             }, reset: function () { | ||||
|                 var a = this._getContext(); | ||||
|                 a.clearRect(0, 0, this.pixelWidth, this.pixelHeight), this.shapes = {}, this.shapeseq = [], this.currentTargetShapeId = c | ||||
|             }, _drawShape: function (a, b, d, e, f) { | ||||
|                 var g = this._getContext(d, e, f), h, i; | ||||
|                 g.beginPath(), g.moveTo(b[0][0] + .5, b[0][1] + .5); | ||||
|                 for (h = 1, i = b.length; h < i; h++) g.lineTo(b[h][0] + .5, b[h][1] + .5); | ||||
|                 d !== c && g.stroke(), e !== c && g.fill(), this.targetX !== c && this.targetY !== c && g.isPointInPath(this.targetX, this.targetY) && (this.currentTargetShapeId = a) | ||||
|             }, _drawCircle: function (a, d, e, f, g, h, i) { | ||||
|                 var j = this._getContext(g, h, i); | ||||
|                 j.beginPath(), j.arc(d, e, f, 0, 2 * b.PI, !1), this.targetX !== c && this.targetY !== c && j.isPointInPath(this.targetX, this.targetY) && (this.currentTargetShapeId = a), g !== c && j.stroke(), h !== c && j.fill() | ||||
|             }, _drawPieSlice: function (a, b, d, e, f, g, h, i) { | ||||
|                 var j = this._getContext(h, i); | ||||
|                 j.beginPath(), j.moveTo(b, d), j.arc(b, d, e, f, g, !1), j.lineTo(b, d), j.closePath(), h !== c && j.stroke(), i && j.fill(), this.targetX !== c && this.targetY !== c && j.isPointInPath(this.targetX, this.targetY) && (this.currentTargetShapeId = a) | ||||
|             }, _drawRect: function (a, b, c, d, e, f, g) { | ||||
|                 return this._drawShape(a, [[b, c], [b + d, c], [b + d, c + e], [b, c + e], [b, c]], f, g) | ||||
|             }, appendShape: function (a) { | ||||
|                 return this.shapes[a.id] = a, this.shapeseq.push(a.id), this.lastShapeId = a.id, a.id | ||||
|             }, replaceWithShape: function (a, b) { | ||||
|                 var c = this.shapeseq, d; | ||||
|                 this.shapes[b.id] = b; | ||||
|                 for (d = c.length; d--;) c[d] == a && (c[d] = b.id); | ||||
|                 delete this.shapes[a] | ||||
|             }, replaceWithShapes: function (a, b) { | ||||
|                 var c = this.shapeseq, d = {}, e, f, g; | ||||
|                 for (f = a.length; f--;) d[a[f]] = !0; | ||||
|                 for (f = c.length; f--;) e = c[f], d[e] && (c.splice(f, 1), delete this.shapes[e], g = f); | ||||
|                 for (f = b.length; f--;) c.splice(g, 0, b[f].id), this.shapes[b[f].id] = b[f] | ||||
|             }, insertAfterShape: function (a, b) { | ||||
|                 var c = this.shapeseq, d; | ||||
|                 for (d = c.length; d--;) if (c[d] === a) { | ||||
|                     c.splice(d + 1, 0, b.id), this.shapes[b.id] = b; | ||||
|                     return | ||||
|                 } | ||||
|             }, removeShapeId: function (a) { | ||||
|                 var b = this.shapeseq, c; | ||||
|                 for (c = b.length; c--;) if (b[c] === a) { | ||||
|                     b.splice(c, 1); | ||||
|                     break | ||||
|                 } | ||||
|                 delete this.shapes[a] | ||||
|             }, getShapeAt: function (a, b, c) { | ||||
|                 return this.targetX = b, this.targetY = c, this.render(), this.currentTargetShapeId | ||||
|             }, render: function () { | ||||
|                 var a = this.shapeseq, b = this.shapes, c = a.length, d = this._getContext(), e, f, g; | ||||
|                 d.clearRect(0, 0, this.pixelWidth, this.pixelHeight); | ||||
|                 for (g = 0; g < c; g++) e = a[g], f = b[e], this["_draw" + f.type].apply(this, f.args); | ||||
|                 this.interact || (this.shapes = {}, this.shapeseq = []) | ||||
|             } | ||||
|         }), J = g(H, { | ||||
|             init: function (b, c, e) { | ||||
|                 var f; | ||||
|                 J._super.init.call(this, b, c, e), e[0] && (e = e[0]), d.data(e, "_jqs_vcanvas", this), this.canvas = a.createElement("span"), d(this.canvas).css({ | ||||
|                     display: "inline-block", | ||||
|                     position: "relative", | ||||
|                     overflow: "hidden", | ||||
|                     width: b, | ||||
|                     height: c, | ||||
|                     margin: "0px", | ||||
|                     padding: "0px", | ||||
|                     verticalAlign: "top" | ||||
|                 }), this._insert(this.canvas, e), this._calculatePixelDims(b, c, this.canvas), this.canvas.width = this.pixelWidth, this.canvas.height = this.pixelHeight, f = '<v:group coordorigin="0 0" coordsize="' + this.pixelWidth + " " + this.pixelHeight + '"' + ' style="position:absolute;top:0;left:0;width:' + this.pixelWidth + "px;height=" + this.pixelHeight + 'px;"></v:group>', this.canvas.insertAdjacentHTML("beforeEnd", f), this.group = d(this.canvas).children()[0], this.rendered = !1, this.prerender = "" | ||||
|             }, _drawShape: function (a, b, d, e, f) { | ||||
|                 var g = [], h, i, j, k, l, m, n; | ||||
|                 for (n = 0, m = b.length; n < m; n++) g[n] = "" + b[n][0] + "," + b[n][1]; | ||||
|                 return h = g.splice(0, 1), f = f === c ? 1 : f, i = d === c ? ' stroked="false" ' : ' strokeWeight="' + f + 'px" strokeColor="' + d + '" ', j = e === c ? ' filled="false"' : ' fillColor="' + e + '" filled="true" ', k = g[0] === g[g.length - 1] ? "x " : "", l = '<v:shape coordorigin="0 0" coordsize="' + this.pixelWidth + " " + this.pixelHeight + '" ' + ' id="jqsshape' + a + '" ' + i + j + ' style="position:absolute;left:0px;top:0px;height:' + this.pixelHeight + "px;width:" + this.pixelWidth + 'px;padding:0px;margin:0px;" ' + ' path="m ' + h + " l " + g.join(", ") + " " + k + 'e">' + " </v:shape>", l | ||||
|             }, _drawCircle: function (a, b, d, e, f, g, h) { | ||||
|                 var i, j, k; | ||||
|                 return b -= e, d -= e, i = f === c ? ' stroked="false" ' : ' strokeWeight="' + h + 'px" strokeColor="' + f + '" ', j = g === c ? ' filled="false"' : ' fillColor="' + g + '" filled="true" ', k = '<v:oval  id="jqsshape' + a + '" ' + i + j + ' style="position:absolute;top:' + d + "px; left:" + b + "px; width:" + e * 2 + "px; height:" + e * 2 + 'px"></v:oval>', k | ||||
|             }, _drawPieSlice: function (a, d, e, f, g, h, i, j) { | ||||
|                 var k, l, m, n, o, p, q, r; | ||||
|                 if (g === h) return ""; | ||||
|                 h - g === 2 * b.PI && (g = 0, h = 2 * b.PI), l = d + b.round(b.cos(g) * f), m = e + b.round(b.sin(g) * f), n = d + b.round(b.cos(h) * f), o = e + b.round(b.sin(h) * f); | ||||
|                 if (l === n && m === o) { | ||||
|                     if (h - g < b.PI) return ""; | ||||
|                     l = n = d + f, m = o = e | ||||
|                 } | ||||
|                 return l === n && m === o && h - g < b.PI ? "" : (k = [d - f, e - f, d + f, e + f, l, m, n, o], p = i === c ? ' stroked="false" ' : ' strokeWeight="1px" strokeColor="' + i + '" ', q = j === c ? ' filled="false"' : ' fillColor="' + j + '" filled="true" ', r = '<v:shape coordorigin="0 0" coordsize="' + this.pixelWidth + " " + this.pixelHeight + '" ' + ' id="jqsshape' + a + '" ' + p + q + ' style="position:absolute;left:0px;top:0px;height:' + this.pixelHeight + "px;width:" + this.pixelWidth + 'px;padding:0px;margin:0px;" ' + ' path="m ' + d + "," + e + " wa " + k.join(", ") + ' x e">' + " </v:shape>", r) | ||||
|             }, _drawRect: function (a, b, c, d, e, f, g) { | ||||
|                 return this._drawShape(a, [[b, c], [b, c + e], [b + d, c + e], [b + d, c], [b, c]], f, g) | ||||
|             }, reset: function () { | ||||
|                 this.group.innerHTML = "" | ||||
|             }, appendShape: function (a) { | ||||
|                 var b = this["_draw" + a.type].apply(this, a.args); | ||||
|                 return this.rendered ? this.group.insertAdjacentHTML("beforeEnd", b) : this.prerender += b, this.lastShapeId = a.id, a.id | ||||
|             }, replaceWithShape: function (a, b) { | ||||
|                 var c = d("#jqsshape" + a), e = this["_draw" + b.type].apply(this, b.args); | ||||
|                 c[0].outerHTML = e | ||||
|             }, replaceWithShapes: function (a, b) { | ||||
|                 var c = d("#jqsshape" + a[0]), e = "", f = b.length, g; | ||||
|                 for (g = 0; g < f; g++) e += this["_draw" + b[g].type].apply(this, b[g].args); | ||||
|                 c[0].outerHTML = e; | ||||
|                 for (g = 1; g < a.length; g++) d("#jqsshape" + a[g]).remove() | ||||
|             }, insertAfterShape: function (a, b) { | ||||
|                 var c = d("#jqsshape" + a), e = this["_draw" + b.type].apply(this, b.args); | ||||
|                 c[0].insertAdjacentHTML("afterEnd", e) | ||||
|             }, removeShapeId: function (a) { | ||||
|                 var b = d("#jqsshape" + a); | ||||
|                 this.group.removeChild(b[0]) | ||||
|             }, getShapeAt: function (a, b, c) { | ||||
|                 var d = a.id.substr(8); | ||||
|                 return d | ||||
|             }, render: function () { | ||||
|                 this.rendered || (this.group.innerHTML = this.prerender, this.rendered = !0) | ||||
|             } | ||||
|         }) | ||||
|     }) | ||||
| })(document, Math); | ||||
							
								
								
									
										1021
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_interface/static/js/material.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1021
									
								
								Vagrant/resources/malcolm/sensor-iso/interface/sensor_interface/static/js/material.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1 @@ | ||||
|  | ||||
| @@ -0,0 +1,28 @@ | ||||
| # Copyright (c) 2021 Battelle Energy Alliance, LLC.  All rights reserved. | ||||
|  | ||||
| import subprocess, json | ||||
| import os | ||||
|  | ||||
| # traverse back up the path of the project directory to the scripts location | ||||
| path = os.path.join(os.path.sep.join(__file__.split(os.path.sep)[:-3]), "sensor_ctl") | ||||
|  | ||||
|  | ||||
| def service(command): | ||||
|  | ||||
|     #TODO implement better error handling | ||||
|  | ||||
|     command, arguement = command.split(" ") | ||||
|  | ||||
|     command_line = os.path.join(path, command) | ||||
|  | ||||
|     print(command_line, arguement) | ||||
|  | ||||
|     p = subprocess.Popen([command_line, arguement], stdout=subprocess.PIPE, stderr=subprocess.PIPE) | ||||
|     retcode = p.wait() | ||||
|     out, err = p.communicate() | ||||
|  | ||||
|     if not retcode: | ||||
|         return json.dumps(out.decode("utf-8")) | ||||
|     else: | ||||
|         return json.dumps( | ||||
|             {"cmd": [command_line], "returncode": retcode, "out": out.decode("utf-8"), "err": err.decode("utf-8")}) | ||||
| @@ -0,0 +1,44 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|     <link rel="stylesheet" href="{{ url_for('static', filename='css/freeboard.min.css') }}"> | ||||
|     <link rel="stylesheet" href="{{ url_for('static', filename='css/material.css') }}"> | ||||
|     <link rel="stylesheet" href="{{ url_for('static', filename='css/custom.css') }}"> | ||||
| </head> | ||||
| <body> | ||||
|  | ||||
| <div class="mdl-layout mdl-js-layout mdl-layout--fixed-header"> | ||||
|     <header class="mdl-layout__header"> | ||||
|         <div class="mdl-layout__header-row"> | ||||
|             <!-- Title --> | ||||
|             <span class="mdl-layout-title">Sensor Menu</span> | ||||
|             <!-- Add spacer, to align navigation to the right --> | ||||
|             <div class="mdl-layout-spacer"></div> | ||||
|             <!-- Navigation. We hide it in small screens. --> | ||||
|             <nav class="mdl-navigation mdl-layout--large-screen-only"> | ||||
|                 <a class="mdl-navigation__link" href="/buttons">Services</a> | ||||
|                 <a class="mdl-navigation__link" href="/">System</a> | ||||
|             </nav> | ||||
|         </div> | ||||
|     </header> | ||||
|     <div class="mdl-layout__drawer"> | ||||
|         <span class="mdl-layout-title">Menu</span> | ||||
|         <nav class="mdl-navigation"> | ||||
|             <a class="mdl-navigation__link" href="/buttons">Services</a> | ||||
|             <a class="mdl-navigation__link" href="/">System</a> | ||||
|         </nav> | ||||
|     </div> | ||||
|     <main class="mdl-layout__content"> | ||||
|         <div class="page-content">{% block content %}{% endblock %}</div> | ||||
|     </main> | ||||
|     <div class="mdl-mega-footer"></div> | ||||
| </div> | ||||
|  | ||||
|  | ||||
| {% block scripts %} | ||||
|     <script src="{{ url_for('static', filename='js/custom.js') }}"></script> | ||||
|     <script src="{{ url_for('static', filename='js/material.min.js') }}"></script> | ||||
| {% endblock %} | ||||
|  | ||||
| </body> | ||||
| </html> | ||||
| @@ -0,0 +1,195 @@ | ||||
| {% extends "base.html" %} | ||||
|  | ||||
|  | ||||
| {% block content %} | ||||
|  | ||||
|     <div class="mdl-grid"> | ||||
|         <div class="mdl-layout-spacer"></div> | ||||
|         <div class="mdl-cell--2-col mdl-cell--stretch"> | ||||
|             <div class="square_card square_card_start mdl-card mdl-shadow--2dp"> | ||||
|                 <div class="mdl-card__title mdl-card--expand"> | ||||
|                     <h3 class="mdl-card__title-text"> | ||||
|                         Services Start | ||||
|                     </h3> | ||||
|                 </div> | ||||
|                 <div class="mdl-card__supporting-text"> | ||||
|                     Start all services | ||||
|                 </div> | ||||
|                 <div class="mdl-card__actions mdl-card--border"> | ||||
|                     <a class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect" | ||||
|                        onclick="start_all()"> | ||||
|                         Start | ||||
|                     </a> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="mdl-layout-spacer"></div> | ||||
|         <div class="mdl-cell--2-col mdl-cell--stretch"> | ||||
|             <div class="square_card square_card_start mdl-card mdl-shadow--2dp"> | ||||
|                 <div class="mdl-card__title mdl-card--expand"> | ||||
|                     <h3 class="mdl-card__title-text"> | ||||
|                         PCAP Start | ||||
|                     </h3> | ||||
|                 </div> | ||||
|                 <div class="mdl-card__supporting-text"> | ||||
|                     Start packet capture | ||||
|                 </div> | ||||
|                 <div class="mdl-card__actions mdl-card--border"> | ||||
|                     <a class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect" | ||||
|                        onclick="start_tcp()"> | ||||
|                         Start | ||||
|                     </a> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="mdl-layout-spacer"></div> | ||||
|         <div class="mdl-cell--2-col mdl-cell--stretch"> | ||||
|             <div class="square_card square_card_start mdl-card mdl-shadow--2dp"> | ||||
|                 <div class="mdl-card__title mdl-card--expand"> | ||||
|                     <h3 class="mdl-card__title-text"> | ||||
|                         Zeek Start | ||||
|                     </h3> | ||||
|                 </div> | ||||
|                 <div class="mdl-card__supporting-text"> | ||||
|                     Start Zeek traffic analysis | ||||
|                 </div> | ||||
|                 <div class="mdl-card__actions mdl-card--border"> | ||||
|                     <a class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect" | ||||
|                        onclick="start_bro()"> | ||||
|                         Start | ||||
|                     </a> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="mdl-layout-spacer"></div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="mdl-grid"> | ||||
|         <div class="mdl-layout-spacer"></div> | ||||
|         <div class="mdl-cell--2-col mdl-cell--stretch"> | ||||
|             <div class="square_card square_card_stop mdl-card mdl-shadow--2dp"> | ||||
|                 <div class="mdl-card__title mdl-card--expand"> | ||||
|                     <h3 class="mdl-card__title-text"> | ||||
|                         Services Stop | ||||
|                     </h3> | ||||
|                 </div> | ||||
|                 <div class="mdl-card__supporting-text"> | ||||
|                     Stop all services | ||||
|                 </div> | ||||
|                 <div class="mdl-card__actions mdl-card--border"> | ||||
|                     <a class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect" | ||||
|                        onclick="stop_all()"> | ||||
|                         Stop | ||||
|                     </a> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="mdl-layout-spacer"></div> | ||||
|         <div class="mdl-cell--2-col mdl-cell--stretch"> | ||||
|             <div class="square_card square_card_stop mdl-card mdl-shadow--2dp"> | ||||
|                 <div class="mdl-card__title mdl-card--expand"> | ||||
|                     <h3 class="mdl-card__title-text"> | ||||
|                         PCAP Stop | ||||
|                     </h3> | ||||
|                 </div> | ||||
|                 <div class="mdl-card__supporting-text"> | ||||
|                     Stop packet capture | ||||
|                 </div> | ||||
|                 <div class="mdl-card__actions mdl-card--border"> | ||||
|                     <a class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect" | ||||
|                        onclick="stop_tcp()"> | ||||
|                         Stop | ||||
|                     </a> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="mdl-layout-spacer"></div> | ||||
|         <div class="mdl-cell--2-col mdl-cell--stretch"> | ||||
|             <div class="square_card square_card_stop mdl-card mdl-shadow--2dp"> | ||||
|                 <div class="mdl-card__title mdl-card--expand"> | ||||
|                     <h3 class="mdl-card__title-text"> | ||||
|                         Zeek Stop | ||||
|                     </h3> | ||||
|                 </div> | ||||
|                 <div class="mdl-card__supporting-text"> | ||||
|                     Stop Zeek traffic analysis | ||||
|                 </div> | ||||
|                 <div class="mdl-card__actions mdl-card--border"> | ||||
|                     <a class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect" | ||||
|                        onclick="stop_bro()"> | ||||
|                         Stop | ||||
|                     </a> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="mdl-layout-spacer"></div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="mdl-grid"> | ||||
|         <div class="mdl-layout-spacer"></div> | ||||
|         <div class="mdl-cell--3-col mdl-cell--stretch"> | ||||
|             <div class="square_card square_card_status mdl-card mdl-shadow--2dp"> | ||||
|                 <div class="mdl-card__title mdl-card--expand"> | ||||
|                     <h3 class="mdl-card__title-text"> | ||||
|                         Sensor Status | ||||
|                     </h3> | ||||
|                 </div> | ||||
|                 <div class="mdl-card__supporting-text"> | ||||
|                     Display sensor service status | ||||
|                 </div> | ||||
|                 <div class="mdl-card__actions mdl-card--border"> | ||||
|                     <a class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect" | ||||
|                        onclick="sensor_status()"> | ||||
|                         Status | ||||
|                     </a> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="mdl-layout-spacer"></div> | ||||
|         <div class="mdl-cell--3-col mdl-cell--stretch"> | ||||
|             <div class="square_card square_card_clean mdl-card mdl-shadow--2dp"> | ||||
|                 <div class="mdl-card__title mdl-card--expand"> | ||||
|                     <h3 class="mdl-card__title-text"> | ||||
|                         Clean Sensor | ||||
|                     </h3> | ||||
|                 </div> | ||||
|                 <div class="mdl-card__supporting-text"> | ||||
|                     Remove captured data | ||||
|                 </div> | ||||
|                 <div class="mdl-card__actions mdl-card--border"> | ||||
|                     <a class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect" | ||||
|                        onclick="clean_sensor()"> | ||||
|                         Clean | ||||
|                     </a> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="mdl-layout-spacer"></div> | ||||
|     </div> | ||||
|  | ||||
|     <div id="myModal" class="mdl-card modal mdl-shadow--4dp square_card_system"> | ||||
|         <div class="mdl-card__title mdl-card--expand"> | ||||
|             <h3 class="mdl-card__title-text"> | ||||
|                 System Response | ||||
|             </h3> | ||||
|         </div> | ||||
|         <div id="response_text" class="mdl-card__supporting-text"> | ||||
|             System response text | ||||
|         </div> | ||||
|         <div class="mdl-card__actions mdl-card--border"> | ||||
|             <button id="close" class="mdl-button mdl-button--colored mdl-button--raised" type="button">Close</button> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="modal center" id="myLoading"> | ||||
|         <div class="center"> | ||||
|             <div class="center"> | ||||
|                 <div class="mdl-spinner mdl-js-spinner is-active"></div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|  | ||||
|  | ||||
| {% endblock %} | ||||
|  | ||||
| @@ -0,0 +1,154 @@ | ||||
| {% extends "base.html" %} | ||||
|  | ||||
|  | ||||
|  | ||||
| {% block content %} | ||||
|     <head> | ||||
|         <script src="{{ url_for('static', filename='js/freeboard.thirdparty.min.js') }}"></script> | ||||
|  | ||||
|         <script type="text/javascript"> | ||||
|             head.js("{{ url_for('static', filename='js/freeboard_plugins.min.js') }}", | ||||
|                  // *** Load more plugins here *** | ||||
|                 function(){ | ||||
|                     $(function() | ||||
|                     { //DOM Ready | ||||
|                         freeboard.initialize(false); | ||||
|                             $.getJSON("/load", function(data) { | ||||
|                                 freeboard.loadDashboard(data, function() { | ||||
|                                     freeboard.setEditing(false); | ||||
|                                 }); | ||||
|                             }); | ||||
|                     }); | ||||
|                 }); | ||||
|         </script> | ||||
|     </head> | ||||
|     <body> | ||||
|     <div id="board-content"> | ||||
|         <img id="dash-logo" data-bind="attr:{src: header_image}, visible:header_image()"> | ||||
|         <div class="gridster responsive-column-width"> | ||||
|             <ul data-bind="grid: true"> | ||||
|             </ul> | ||||
|         </div> | ||||
|     </div> | ||||
|     <header id="main-header" data-bind="if:allow_edit"> | ||||
|         <div id="admin-bar"> | ||||
|             <div id="admin-menu"> | ||||
|                 <div id="board-tools"> | ||||
|                     <h1 id="board-logo" class="title bordered">freeboard</h1> | ||||
|                     <div id="board-actions"> | ||||
|                         <ul class="board-toolbar vertical"> | ||||
|                             <li data-bind="click: loadDashboardFromLocalFile"><i id="full-screen-icon" | ||||
|                                                                                  class="icon-folder-open icon-white"></i><label | ||||
|                                     id="full-screen">Load Freeboard</label></li> | ||||
|                             <li><i class="icon-download-alt icon-white"></i> | ||||
|                                 <label data-bind="click: saveDashboardClicked">Save Freeboard</label> | ||||
|                                 <label style="display: none;" data-bind="click: saveDashboard" data-pretty="true">[Pretty]</label> | ||||
|                                 <label style="display: none;" data-bind="click: saveDashboard" data-pretty="false">[Minified]</label> | ||||
|                             </li> | ||||
|                             <li id="add-pane" data-bind="click: createPane"><i class="icon-plus icon-white"></i><label>Add | ||||
|                                 Pane</label></li> | ||||
|                         </ul> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                 <div id="datasources"> | ||||
|                     <h2 class="title">DATASOURCES</h2> | ||||
|  | ||||
|                     <div class="datasource-list-container"> | ||||
|                         <table class="table table-condensed sub-table" id="datasources-list" | ||||
|                                data-bind="if: datasources().length"> | ||||
|                             <thead> | ||||
|                             <tr> | ||||
|                                 <th>Name</th> | ||||
|                                 <th>Last Updated</th> | ||||
|                                 <th> </th> | ||||
|                             </tr> | ||||
|                             </thead> | ||||
|                             <tbody data-bind="foreach: datasources"> | ||||
|                             <tr> | ||||
|                                 <td> | ||||
|                                     <span class="text-button datasource-name" | ||||
|                                           data-bind="text: name, pluginEditor: {operation: 'edit', type: 'datasource'}"></span> | ||||
|                                 </td> | ||||
|                                 <td data-bind="text: last_updated"></td> | ||||
|                                 <td> | ||||
|                                     <ul class="board-toolbar"> | ||||
|                                         <li data-bind="click: updateNow"><i class="icon-refresh icon-white"></i></li> | ||||
|                                         <li data-bind="pluginEditor: {operation: 'delete', type: 'datasource'}"> | ||||
|                                             <i class="icon-trash icon-white"></i></li> | ||||
|                                     </ul> | ||||
|                                 </td> | ||||
|                             </tr> | ||||
|                             </tbody> | ||||
|                         </table> | ||||
|                     </div> | ||||
|                     <span class="text-button table-operation" | ||||
|                           data-bind="pluginEditor: {operation: 'add', type: 'datasource'}">ADD</span> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div id="column-tools" class="responsive-column-width"> | ||||
|             <ul class="board-toolbar left-columns"> | ||||
|                 <li class="column-tool add" data-bind="click: addGridColumnLeft"><span class="column-icon right"></span><i | ||||
|                         class="icon-arrow-left icon-white"></i></li> | ||||
|                 <li class="column-tool sub" data-bind="click: subGridColumnLeft"><span | ||||
|                         class="column-icon left"></span><i class="icon-arrow-right icon-white"></i></li> | ||||
|             </ul> | ||||
|             <ul class="board-toolbar right-columns"> | ||||
|                 <li class="column-tool sub" data-bind="click: subGridColumnRight"><span | ||||
|                         class="column-icon right"></span><i class="icon-arrow-left icon-white"></i></li> | ||||
|                 <li class="column-tool add" data-bind="click: addGridColumnRight"><span class="column-icon left"></span><i | ||||
|                         class="icon-arrow-right icon-white"></i></li> | ||||
|             </ul> | ||||
|         </div> | ||||
|         <div id="toggle-header" data-bind="click: toggleEditing"> | ||||
|             <i id="toggle-header-icon" class="icon-wrench icon-white"></i></div> | ||||
|     </header> | ||||
|  | ||||
|     <div style="display:hidden"> | ||||
|         <ul data-bind="template: { name: 'pane-template', foreach: panes}"> | ||||
|         </ul> | ||||
|     </div> | ||||
|  | ||||
|     <script type="text/html" id="pane-template"> | ||||
|         <li data-bind="pane: true"> | ||||
|             <header> | ||||
|                 <h1 data-bind="text: title"></h1> | ||||
|                 <ul class="board-toolbar pane-tools"> | ||||
|                     <li data-bind="pluginEditor: {operation: 'add', type: 'widget'}"> | ||||
|                         <i class="icon-plus icon-white"></i> | ||||
|                     </li> | ||||
|                     <li data-bind="pluginEditor: {operation: 'edit', type: 'pane'}"> | ||||
|                         <i class="icon-wrench icon-white"></i> | ||||
|                     </li> | ||||
|                     <li data-bind="pluginEditor: {operation: 'delete', type: 'pane'}"> | ||||
|                         <i class="icon-trash icon-white"></i> | ||||
|                     </li> | ||||
|                 </ul> | ||||
|             </header> | ||||
|             <section data-bind="foreach: widgets"> | ||||
|                 <div class="sub-section" data-bind="css: 'sub-section-height-' + height()"> | ||||
|                     <div class="widget" data-bind="widget: true, css:{fillsize:fillSize}"></div> | ||||
|                     <div class="sub-section-tools"> | ||||
|                         <ul class="board-toolbar"> | ||||
|                             <!-- ko if:$parent.widgetCanMoveUp($data) --> | ||||
|                             <li data-bind="click:$parent.moveWidgetUp"><i class="icon-chevron-up icon-white"></i></li> | ||||
|                             <!-- /ko --> | ||||
|                             <!-- ko if:$parent.widgetCanMoveDown($data) --> | ||||
|                             <li data-bind="click:$parent.moveWidgetDown"><i class="icon-chevron-down icon-white"></i> | ||||
|                             </li> | ||||
|                             <!-- /ko --> | ||||
|                             <li data-bind="pluginEditor: {operation: 'edit', type: 'widget'}"><i | ||||
|                                     class="icon-wrench icon-white"></i></li> | ||||
|                             <li data-bind="pluginEditor: {operation: 'delete', type: 'widget'}"><i | ||||
|                                     class="icon-trash icon-white"></i></li> | ||||
|                         </ul> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </section> | ||||
|         </li> | ||||
|     </script> | ||||
|  | ||||
|     </body> | ||||
|  | ||||
|  | ||||
| {% endblock %} | ||||
		Reference in New Issue
	
	Block a user