diff options
-rw-r--r-- | sql/dump.sql | 1075 | ||||
-rw-r--r-- | web/etc/varnish/default.vcl | 2 | ||||
-rw-r--r-- | web/nms.gathering.org/nms2/img/tg15-salkart-clean-big.png | bin | 0 -> 84362 bytes | |||
-rw-r--r-- | web/nms.gathering.org/nms2/index.html | 659 | ||||
-rw-r--r-- | web/nms.gathering.org/nms2/js/nms-color-util.js | 111 | ||||
-rw-r--r-- | web/nms.gathering.org/nms2/js/nms-map-handlers.js | 200 | ||||
-rw-r--r-- | web/nms.gathering.org/nms2/js/nms.js | 578 | ||||
-rwxr-xr-x | web/nms.gathering.org/port-state.pl | 7 | ||||
-rwxr-xr-x | web/nms.gathering.org/uplinkkart-text.pl | 2 |
9 files changed, 2209 insertions, 425 deletions
diff --git a/sql/dump.sql b/sql/dump.sql new file mode 100644 index 0000000..f65c7b9 --- /dev/null +++ b/sql/dump.sql @@ -0,0 +1,1075 @@ +-- +-- PostgreSQL database dump +-- + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SET check_function_bodies = false; +SET client_min_messages = warning; + +-- +-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: +-- + +CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; + + +-- +-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: +-- + +COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; + + +SET search_path = public, pg_catalog; + +-- +-- Name: comment_state; Type: TYPE; Schema: public; Owner: nms +-- + +CREATE TYPE comment_state AS ENUM ( + 'active', + 'inactive', + 'persist', + 'delete' +); + + +ALTER TYPE comment_state OWNER TO nms; + +-- +-- Name: datarate; Type: TYPE; Schema: public; Owner: nms +-- + +CREATE TYPE datarate AS ( + switch integer, + ifname character varying(30), + ifhcinoctets double precision, + ifhcoutoctets double precision, + last_poll_time timestamp with time zone +); + + +ALTER TYPE datarate OWNER TO nms; + +-- +-- Name: operstatuses; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE operstatuses AS ( + switch integer, + ifdescr character(30), + ifoperstatus integer, + last_poll_time timestamp with time zone +); + + +ALTER TYPE operstatuses OWNER TO postgres; + +-- +-- Name: sample; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE sample AS ( + value bigint, + polled timestamp with time zone +); + + +ALTER TYPE sample OWNER TO postgres; + +-- +-- Name: sample_state; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE sample_state AS ( + last sample, + next_last sample +); + + +ALTER TYPE sample_state OWNER TO postgres; + +-- +-- Name: add_new_element(sample[], sample); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION add_new_element(sample[], sample) RETURNS sample[] + LANGUAGE sql + AS $_$ select ('{' || $1[1] || ', ' || $2 || '}')::sample[] $_$; + + +ALTER FUNCTION public.add_new_element(sample[], sample) OWNER TO postgres; + +-- +-- Name: add_new_element(sample_state, sample); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION add_new_element(sample_state, sample) RETURNS sample_state + LANGUAGE sql + AS $_$ + SELECT ($1.next_last, $2)::sample_state +$_$; + + +ALTER FUNCTION public.add_new_element(sample_state, sample) OWNER TO postgres; + +-- +-- Name: get_current_datarate(); Type: FUNCTION; Schema: public; Owner: nms +-- + +CREATE FUNCTION get_current_datarate() RETURNS SETOF datarate + LANGUAGE sql + AS $$ + SELECT switch,ifname, + (ifhcoutoctets[1] - ifhcoutoctets[2]) / EXTRACT(EPOCH FROM (time[1] - time[2])) AS ifhcoutoctets, + (ifhcinoctets[1] - ifhcinoctets[2]) / EXTRACT(EPOCH FROM (time[1] - time[2])) AS ifhcinoctets, + time[1] AS last_poll_time + FROM ( + SELECT switch,ifname, + ARRAY_AGG(time) AS time, + ARRAY_AGG(ifhcinoctets) AS ifhcinoctets, + ARRAY_AGG(ifhcoutoctets) AS ifhcoutoctets + FROM ( + SELECT *,rank() OVER (PARTITION BY switch,ifname ORDER BY time DESC) AS poll_num + FROM polls WHERE time BETWEEN (now() - interval '11 minutes') AND now() + ) t1 + WHERE poll_num <= 2 + GROUP BY switch,ifname + ) t2 + WHERE + time[2] IS NOT NULL + AND ifhcinoctets[1] >= 0 AND ifhcoutoctets[1] >= 0 + AND ifhcinoctets[2] >= 0 AND ifhcoutoctets[2] >= 0 + AND ifhcoutoctets[1] >= ifhcoutoctets[2] + AND ifhcinoctets[1] >= ifhcinoctets[2]; +$$; + + +ALTER FUNCTION public.get_current_datarate() OWNER TO nms; + +-- +-- Name: get_datarate(); Type: FUNCTION; Schema: public; Owner: nms +-- + +CREATE FUNCTION get_datarate() RETURNS SETOF datarate + LANGUAGE plpgsql + AS $$ +DECLARE + num_entries INTEGER; + poll polls; + second_last_poll polls; + last_poll polls; + timediff float; + ret datarate; +BEGIN + num_entries := 0; + last_poll.switch = -1; + + FOR poll IN select * from polls where time >= now() - '15 minutes'::interval and time < now() order by switch,ifname,time LOOP + IF poll.switch <> last_poll.switch OR poll.ifname <> last_poll.ifname THEN + IF num_entries >= 2 THEN + timediff := EXTRACT(epoch from last_poll.time - second_last_poll.time); + ret.switch := last_poll.switch; + ret.ifname := last_poll.ifname; + + IF last_poll.ifhcinoctets < second_last_poll.ifhcinoctets THEN + second_last_poll.ifhcinoctets = 0; + END IF; + IF last_poll.ifhcoutoctets < second_last_poll.ifhcoutoctets THEN + second_last_poll.ifhcoutoctets = 0; + END IF; + + ret.ifhcinoctets := (last_poll.ifhcinoctets - second_last_poll.ifhcinoctets) / timediff; + ret.ifhcoutoctets := (last_poll.ifhcoutoctets - second_last_poll.ifhcoutoctets) / timediff; + ret.last_poll_time := last_poll.time; + return next ret; + ELSIF num_entries = 1 THEN + ret.switch := last_poll.switch; + ret.ifname := last_poll.ifname; + ret.ifhcinoctets := -1; + ret.ifhcoutoctets := -1; + ret.last_poll_time := last_poll.time; + return next ret; + END IF; + num_entries := 1; + ELSE + num_entries := num_entries + 1; + END IF; + second_last_poll.switch := last_poll.switch; + second_last_poll.ifname := last_poll.ifname; + second_last_poll.time := last_poll.time; + second_last_poll.ifhcinoctets := last_poll.ifhcinoctets; + second_last_poll.ifhcoutoctets := last_poll.ifhcoutoctets; + last_poll.switch := poll.switch; + last_poll.ifname := poll.ifname; + last_poll.time := poll.time; + last_poll.ifhcinoctets := poll.ifhcinoctets; + last_poll.ifhcoutoctets := poll.ifhcoutoctets; + END LOOP; + -- pah, and once more, for the last switch/ifname... + IF num_entries >= 2 THEN + timediff := EXTRACT(epoch from last_poll.time - second_last_poll.time); + ret.switch := last_poll.switch; + ret.ifname := last_poll.ifname; + + IF last_poll.ifhcinoctets < second_last_poll.ifhcinoctets THEN + second_last_poll.ifhcinoctets = 0; + END IF; + IF last_poll.ifhcoutoctets < second_last_poll.ifhcoutoctets THEN + second_last_poll.ifhcoutoctets = 0; + END IF; + + ret.ifhcinoctets := (last_poll.ifhcinoctets - second_last_poll.ifhcinoctets) / timediff; + ret.ifhcoutoctets := (last_poll.ifhcoutoctets - second_last_poll.ifhcoutoctets) / timediff; + ret.last_poll_time := last_poll.time; + return next ret; + ELSIF num_entries = 1 THEN + ret.switch := last_poll.switch; + ret.ifname := last_poll.ifname; + ret.ifhcinoctets := -1; + ret.ifhcoutoctets := -1; + ret.last_poll_time := last_poll.time; + return next ret; + END IF; + + RETURN; +END; +$$; + + +ALTER FUNCTION public.get_datarate() OWNER TO nms; + +-- +-- Name: current_change(sample); Type: AGGREGATE; Schema: public; Owner: postgres +-- + +CREATE AGGREGATE current_change(sample) ( + SFUNC = public.add_new_element, + STYPE = sample_state +); + + +ALTER AGGREGATE public.current_change(sample) OWNER TO postgres; + +SET default_tablespace = ''; + +SET default_with_oids = false; + +-- +-- Name: ap_poll; Type: TABLE; Schema: public; Owner: nms; Tablespace: +-- + +CREATE TABLE ap_poll ( + switch integer NOT NULL, + model character varying DEFAULT ''::character varying NOT NULL, + last_poll timestamp with time zone +); + + +ALTER TABLE ap_poll OWNER TO nms; + +-- +-- Name: backup_polls; Type: TABLE; Schema: public; Owner: nms; Tablespace: +-- + +CREATE TABLE backup_polls ( + "time" timestamp with time zone, + switch integer, + port integer, + bytes_in bigint, + bytes_out bigint, + errors_in bigint, + errors_out bigint +); + + +ALTER TABLE backup_polls OWNER TO nms; + +-- +-- Name: cpuloadpoll_id_seq; Type: SEQUENCE; Schema: public; Owner: nms +-- + +CREATE SEQUENCE cpuloadpoll_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE cpuloadpoll_id_seq OWNER TO nms; + +-- +-- Name: cpuloadpoll; Type: TABLE; Schema: public; Owner: nms; Tablespace: +-- + +CREATE TABLE cpuloadpoll ( + id integer DEFAULT nextval('cpuloadpoll_id_seq'::regclass) NOT NULL, + "time" timestamp without time zone NOT NULL, + switch integer NOT NULL, + entity integer NOT NULL, + value integer NOT NULL +); + + +ALTER TABLE cpuloadpoll OWNER TO nms; + +-- +-- Name: dhcp; Type: TABLE; Schema: public; Owner: nms; Tablespace: +-- + +CREATE TABLE dhcp ( + switch integer NOT NULL, + network cidr NOT NULL, + last_ack timestamp without time zone, + owner_color character varying +); + + +ALTER TABLE dhcp OWNER TO nms; + +-- +-- Name: linknet_ping; Type: TABLE; Schema: public; Owner: nms; Tablespace: +-- + +CREATE TABLE linknet_ping ( + linknet integer NOT NULL, + updated timestamp with time zone DEFAULT now() NOT NULL, + latency1_ms double precision, + latency2_ms double precision +); + + +ALTER TABLE linknet_ping OWNER TO nms; + +-- +-- Name: linknets; Type: TABLE; Schema: public; Owner: nms; Tablespace: +-- + +CREATE TABLE linknets ( + linknet integer NOT NULL, + switch1 integer NOT NULL, + addr1 inet NOT NULL, + switch2 integer NOT NULL, + addr2 inet NOT NULL +); + + +ALTER TABLE linknets OWNER TO nms; + +-- +-- Name: linknets_linknet_seq; Type: SEQUENCE; Schema: public; Owner: nms +-- + +CREATE SEQUENCE linknets_linknet_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE linknets_linknet_seq OWNER TO nms; + +-- +-- Name: linknets_linknet_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: nms +-- + +ALTER SEQUENCE linknets_linknet_seq OWNED BY linknets.linknet; + + +-- +-- Name: mbd_log; Type: TABLE; Schema: public; Owner: nms; Tablespace: +-- + +CREATE TABLE mbd_log ( + ts timestamp without time zone, + game character varying, + port integer, + description character varying, + active_servers integer +); + + +ALTER TABLE mbd_log OWNER TO nms; + +-- +-- Name: mldpolls; Type: TABLE; Schema: public; Owner: postgres; Tablespace: +-- + +CREATE TABLE mldpolls ( + "time" timestamp with time zone NOT NULL, + switch integer NOT NULL, + mcast_group inet NOT NULL, + count integer NOT NULL, + raw_portlist character varying +); + + +ALTER TABLE mldpolls OWNER TO postgres; + +-- +-- Name: pgbench_accounts; Type: TABLE; Schema: public; Owner: postgres; Tablespace: +-- + +CREATE TABLE pgbench_accounts ( + aid integer NOT NULL, + bid integer, + abalance integer, + filler character(84) +) +WITH (fillfactor=100); + + +ALTER TABLE pgbench_accounts OWNER TO postgres; + +-- +-- Name: pgbench_branches; Type: TABLE; Schema: public; Owner: postgres; Tablespace: +-- + +CREATE TABLE pgbench_branches ( + bid integer NOT NULL, + bbalance integer, + filler character(88) +) +WITH (fillfactor=100); + + +ALTER TABLE pgbench_branches OWNER TO postgres; + +-- +-- Name: pgbench_history; Type: TABLE; Schema: public; Owner: postgres; Tablespace: +-- + +CREATE TABLE pgbench_history ( + tid integer, + bid integer, + aid integer, + delta integer, + mtime timestamp without time zone, + filler character(22) +); + + +ALTER TABLE pgbench_history OWNER TO postgres; + +-- +-- Name: pgbench_tellers; Type: TABLE; Schema: public; Owner: postgres; Tablespace: +-- + +CREATE TABLE pgbench_tellers ( + tid integer NOT NULL, + bid integer, + tbalance integer, + filler character(84) +) +WITH (fillfactor=100); + + +ALTER TABLE pgbench_tellers OWNER TO postgres; + +-- +-- Name: ping; Type: TABLE; Schema: public; Owner: nms; Tablespace: +-- + +CREATE TABLE ping ( + switch integer NOT NULL, + updated timestamp with time zone DEFAULT now() NOT NULL, + latency_ms double precision +); + + +ALTER TABLE ping OWNER TO nms; + +-- +-- Name: ping_secondary_ip; Type: TABLE; Schema: public; Owner: nms; Tablespace: +-- + +CREATE TABLE ping_secondary_ip ( + switch integer NOT NULL, + updated timestamp with time zone DEFAULT now() NOT NULL, + latency_ms double precision +); + + +ALTER TABLE ping_secondary_ip OWNER TO nms; + +-- +-- Name: placements; Type: TABLE; Schema: public; Owner: nms; Tablespace: +-- + +CREATE TABLE placements ( + switch integer NOT NULL, + placement box NOT NULL, + zorder integer DEFAULT 0 NOT NULL +); + + +ALTER TABLE placements OWNER TO nms; + +-- +-- Name: polls; Type: TABLE; Schema: public; Owner: nms; Tablespace: +-- + +CREATE TABLE polls ( + switch integer NOT NULL, + "time" timestamp with time zone NOT NULL, + ifname character varying(30) NOT NULL, + ifhighspeed integer, + ifhcoutoctets bigint, + ifhcinoctets bigint +); + + +ALTER TABLE polls OWNER TO nms; + +-- +-- Name: portnames; Type: TABLE; Schema: public; Owner: nms; Tablespace: +-- + +CREATE TABLE portnames ( + switchtype character varying NOT NULL, + port integer NOT NULL, + description character varying NOT NULL +); + + +ALTER TABLE portnames OWNER TO nms; + +-- +-- Name: seen_mac; Type: TABLE; Schema: public; Owner: postgres; Tablespace: +-- + +CREATE TABLE seen_mac ( + mac macaddr NOT NULL, + address inet NOT NULL, + seen timestamp with time zone DEFAULT now() NOT NULL +); + + +ALTER TABLE seen_mac OWNER TO postgres; + +-- +-- Name: squeue; Type: TABLE; Schema: public; Owner: nms; Tablespace: +-- + +CREATE TABLE squeue ( + id integer DEFAULT nextval(('squeue_sequence'::text)::regclass) NOT NULL, + gid integer NOT NULL, + added timestamp with time zone NOT NULL, + updated timestamp with time zone, + addr inet, + cmd character varying NOT NULL, + locked boolean DEFAULT false NOT NULL, + processed boolean DEFAULT false NOT NULL, + disabled boolean DEFAULT false NOT NULL, + priority integer DEFAULT 3, + sysname character varying NOT NULL, + author character varying NOT NULL, + result character varying, + delay timestamp with time zone, + delaytime interval DEFAULT '00:01:00'::interval +); + + +ALTER TABLE squeue OWNER TO nms; + +-- +-- Name: squeue_group_sequence; Type: SEQUENCE; Schema: public; Owner: nms +-- + +CREATE SEQUENCE squeue_group_sequence + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE squeue_group_sequence OWNER TO nms; + +-- +-- Name: squeue_sequence; Type: SEQUENCE; Schema: public; Owner: nms +-- + +CREATE SEQUENCE squeue_sequence + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE squeue_sequence OWNER TO nms; + +-- +-- Name: stemppoll_sequence; Type: SEQUENCE; Schema: public; Owner: nms +-- + +CREATE SEQUENCE stemppoll_sequence + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE stemppoll_sequence OWNER TO nms; + +-- +-- Name: switch_comments; Type: TABLE; Schema: public; Owner: nms; Tablespace: +-- + +CREATE TABLE switch_comments ( + switch integer NOT NULL, + "time" timestamp with time zone, + comment text, + state comment_state DEFAULT 'active'::comment_state, + username character varying(32), + id integer NOT NULL +); + + +ALTER TABLE switch_comments OWNER TO nms; + +-- +-- Name: switch_comments_id_seq; Type: SEQUENCE; Schema: public; Owner: nms +-- + +CREATE SEQUENCE switch_comments_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE switch_comments_id_seq OWNER TO nms; + +-- +-- Name: switch_comments_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: nms +-- + +ALTER SEQUENCE switch_comments_id_seq OWNED BY switch_comments.id; + + +-- +-- Name: switch_temp; Type: TABLE; Schema: public; Owner: nms; Tablespace: +-- + +CREATE TABLE switch_temp ( + switch integer, + temp integer, + "time" timestamp with time zone +); + + +ALTER TABLE switch_temp OWNER TO nms; + +-- +-- Name: switches; Type: TABLE; Schema: public; Owner: nms; Tablespace: +-- + +CREATE TABLE switches ( + switch integer DEFAULT nextval(('"switches_switch_seq"'::text)::regclass) NOT NULL, + ip inet NOT NULL, + sysname character varying NOT NULL, + switchtype character varying NOT NULL, + last_updated timestamp with time zone, + locked boolean DEFAULT false NOT NULL, + priority integer DEFAULT 0 NOT NULL, + poll_frequency interval DEFAULT '00:01:00'::interval NOT NULL, + community character varying DEFAULT 'public'::character varying NOT NULL, + lldp_chassis_id character varying, + secondary_ip inet +); + + +ALTER TABLE switches OWNER TO nms; + +-- +-- Name: switches_switch_seq; Type: SEQUENCE; Schema: public; Owner: nms +-- + +CREATE SEQUENCE switches_switch_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE switches_switch_seq OWNER TO nms; + +-- +-- Name: switchtypes; Type: TABLE; Schema: public; Owner: nms; Tablespace: +-- + +CREATE TABLE switchtypes ( + switchtype character varying NOT NULL, + ports character varying NOT NULL +); + + +ALTER TABLE switchtypes OWNER TO nms; + +-- +-- Name: temppoll; Type: TABLE; Schema: public; Owner: nms; Tablespace: +-- + +CREATE TABLE temppoll ( + id integer DEFAULT nextval(('stemppoll_sequence'::text)::regclass) NOT NULL, + "time" timestamp without time zone NOT NULL, + switch integer NOT NULL, + temp double precision +); + + +ALTER TABLE temppoll OWNER TO nms; + +-- +-- Name: uplinks; Type: TABLE; Schema: public; Owner: nms; Tablespace: +-- + +CREATE TABLE uplinks ( + switch integer NOT NULL, + coreswitch integer NOT NULL, + blade integer NOT NULL, + port integer NOT NULL +); + + +ALTER TABLE uplinks OWNER TO nms; + +-- +-- Name: linknet; Type: DEFAULT; Schema: public; Owner: nms +-- + +ALTER TABLE ONLY linknets ALTER COLUMN linknet SET DEFAULT nextval('linknets_linknet_seq'::regclass); + + +-- +-- Name: id; Type: DEFAULT; Schema: public; Owner: nms +-- + +ALTER TABLE ONLY switch_comments ALTER COLUMN id SET DEFAULT nextval('switch_comments_id_seq'::regclass); + + +-- +-- Name: cpuloadpoll_pkey; Type: CONSTRAINT; Schema: public; Owner: nms; Tablespace: +-- + +ALTER TABLE ONLY cpuloadpoll + ADD CONSTRAINT cpuloadpoll_pkey PRIMARY KEY (id); + + +-- +-- Name: pgbench_accounts_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace: +-- + +ALTER TABLE ONLY pgbench_accounts + ADD CONSTRAINT pgbench_accounts_pkey PRIMARY KEY (aid); + + +-- +-- Name: pgbench_branches_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace: +-- + +ALTER TABLE ONLY pgbench_branches + ADD CONSTRAINT pgbench_branches_pkey PRIMARY KEY (bid); + + +-- +-- Name: pgbench_tellers_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace: +-- + +ALTER TABLE ONLY pgbench_tellers + ADD CONSTRAINT pgbench_tellers_pkey PRIMARY KEY (tid); + + +-- +-- Name: polls_time_switch_ifname_key; Type: CONSTRAINT; Schema: public; Owner: nms; Tablespace: +-- + +ALTER TABLE ONLY polls + ADD CONSTRAINT polls_time_switch_ifname_key UNIQUE ("time", switch, ifname); + + +-- +-- Name: seen_mac_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace: +-- + +ALTER TABLE ONLY seen_mac + ADD CONSTRAINT seen_mac_pkey PRIMARY KEY (mac, address, seen); + + +-- +-- Name: switch_comments_pkey; Type: CONSTRAINT; Schema: public; Owner: nms; Tablespace: +-- + +ALTER TABLE ONLY switch_comments + ADD CONSTRAINT switch_comments_pkey PRIMARY KEY (id); + + +-- +-- Name: switches_pkey; Type: CONSTRAINT; Schema: public; Owner: nms; Tablespace: +-- + +ALTER TABLE ONLY switches + ADD CONSTRAINT switches_pkey PRIMARY KEY (switch); + + +-- +-- Name: switches_sysname_key; Type: CONSTRAINT; Schema: public; Owner: nms; Tablespace: +-- + +ALTER TABLE ONLY switches + ADD CONSTRAINT switches_sysname_key UNIQUE (sysname); + + +-- +-- Name: switches_sysname_key1; Type: CONSTRAINT; Schema: public; Owner: nms; Tablespace: +-- + +ALTER TABLE ONLY switches + ADD CONSTRAINT switches_sysname_key1 UNIQUE (sysname); + + +-- +-- Name: switchtypes_pkey; Type: CONSTRAINT; Schema: public; Owner: nms; Tablespace: +-- + +ALTER TABLE ONLY switchtypes + ADD CONSTRAINT switchtypes_pkey PRIMARY KEY (switchtype); + + +-- +-- Name: ping_index; Type: INDEX; Schema: public; Owner: nms; Tablespace: +-- + +CREATE INDEX ping_index ON ping USING btree (updated); + + +-- +-- Name: polls_time; Type: INDEX; Schema: public; Owner: nms; Tablespace: +-- + +CREATE INDEX polls_time ON polls USING btree ("time"); + + +-- +-- Name: seen_mac_addr_family; Type: INDEX; Schema: public; Owner: postgres; Tablespace: +-- + +CREATE INDEX seen_mac_addr_family ON seen_mac USING btree (family(address)); + + +-- +-- Name: seen_mac_seen; Type: INDEX; Schema: public; Owner: postgres; Tablespace: +-- + +CREATE INDEX seen_mac_seen ON seen_mac USING btree (seen); + + +-- +-- Name: switch_temp_index; Type: INDEX; Schema: public; Owner: nms; Tablespace: +-- + +CREATE INDEX switch_temp_index ON switch_temp USING btree (switch); + + +-- +-- Name: switches_ap_poll; Type: INDEX; Schema: public; Owner: nms; Tablespace: +-- + +CREATE UNIQUE INDEX switches_ap_poll ON ap_poll USING btree (switch); + + +-- +-- Name: switches_dhcp; Type: INDEX; Schema: public; Owner: nms; Tablespace: +-- + +CREATE UNIQUE INDEX switches_dhcp ON dhcp USING btree (switch); + + +-- +-- Name: switches_placement; Type: INDEX; Schema: public; Owner: nms; Tablespace: +-- + +CREATE UNIQUE INDEX switches_placement ON placements USING btree (switch); + + +-- +-- Name: switches_switch; Type: INDEX; Schema: public; Owner: nms; Tablespace: +-- + +CREATE INDEX switches_switch ON switches USING hash (switch); + + +-- +-- Name: temppoll_search; Type: INDEX; Schema: public; Owner: nms; Tablespace: +-- + +CREATE INDEX temppoll_search ON temppoll USING btree (switch, id); + + +-- +-- Name: updated_index2; Type: INDEX; Schema: public; Owner: nms; Tablespace: +-- + +CREATE INDEX updated_index2 ON linknet_ping USING btree (updated); + + +-- +-- Name: updated_index3; Type: INDEX; Schema: public; Owner: nms; Tablespace: +-- + +CREATE INDEX updated_index3 ON ping_secondary_ip USING btree (updated); + + +-- +-- Name: ap_poll_switch_fkey; Type: FK CONSTRAINT; Schema: public; Owner: nms +-- + +ALTER TABLE ONLY ap_poll + ADD CONSTRAINT ap_poll_switch_fkey FOREIGN KEY (switch) REFERENCES switches(switch); + + +-- +-- Name: switches_switchtype_fkey; Type: FK CONSTRAINT; Schema: public; Owner: nms +-- + +ALTER TABLE ONLY switches + ADD CONSTRAINT switches_switchtype_fkey FOREIGN KEY (switchtype) REFERENCES switchtypes(switchtype); + + +-- +-- Name: switchname; Type: FK CONSTRAINT; Schema: public; Owner: nms +-- + +ALTER TABLE ONLY polls + ADD CONSTRAINT switchname FOREIGN KEY (switch) REFERENCES switches(switch); + + +-- +-- Name: switchname; Type: FK CONSTRAINT; Schema: public; Owner: nms +-- + +ALTER TABLE ONLY ping + ADD CONSTRAINT switchname FOREIGN KEY (switch) REFERENCES switches(switch); + + +-- +-- Name: switchname; Type: FK CONSTRAINT; Schema: public; Owner: nms +-- + +ALTER TABLE ONLY switch_comments + ADD CONSTRAINT switchname FOREIGN KEY (switch) REFERENCES switches(switch); + + +-- +-- Name: temppoll_switch_fkey; Type: FK CONSTRAINT; Schema: public; Owner: nms +-- + +ALTER TABLE ONLY temppoll + ADD CONSTRAINT temppoll_switch_fkey FOREIGN KEY (switch) REFERENCES switches(switch); + + +-- +-- Name: uplinks_coreswitch_fkey; Type: FK CONSTRAINT; Schema: public; Owner: nms +-- + +ALTER TABLE ONLY uplinks + ADD CONSTRAINT uplinks_coreswitch_fkey FOREIGN KEY (coreswitch) REFERENCES switches(switch); + + +-- +-- Name: uplinks_switch_fkey; Type: FK CONSTRAINT; Schema: public; Owner: nms +-- + +ALTER TABLE ONLY uplinks + ADD CONSTRAINT uplinks_switch_fkey FOREIGN KEY (switch) REFERENCES switches(switch); + + +-- +-- Name: public; Type: ACL; Schema: -; Owner: postgres +-- + +REVOKE ALL ON SCHEMA public FROM PUBLIC; +REVOKE ALL ON SCHEMA public FROM postgres; +GRANT ALL ON SCHEMA public TO postgres; +GRANT ALL ON SCHEMA public TO PUBLIC; + + +-- +-- Name: dhcp; Type: ACL; Schema: public; Owner: nms +-- + +REVOKE ALL ON TABLE dhcp FROM PUBLIC; +REVOKE ALL ON TABLE dhcp FROM nms; +GRANT ALL ON TABLE dhcp TO nms; + + +-- +-- Name: mbd_log; Type: ACL; Schema: public; Owner: nms +-- + +REVOKE ALL ON TABLE mbd_log FROM PUBLIC; +REVOKE ALL ON TABLE mbd_log FROM nms; +GRANT ALL ON TABLE mbd_log TO nms; + + +-- +-- Name: mldpolls; Type: ACL; Schema: public; Owner: postgres +-- + +REVOKE ALL ON TABLE mldpolls FROM PUBLIC; +REVOKE ALL ON TABLE mldpolls FROM postgres; +GRANT ALL ON TABLE mldpolls TO postgres; +GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE mldpolls TO nms; + + +-- +-- Name: placements; Type: ACL; Schema: public; Owner: nms +-- + +REVOKE ALL ON TABLE placements FROM PUBLIC; +REVOKE ALL ON TABLE placements FROM nms; +GRANT ALL ON TABLE placements TO nms; + + +-- +-- Name: seen_mac; Type: ACL; Schema: public; Owner: postgres +-- + +REVOKE ALL ON TABLE seen_mac FROM PUBLIC; +REVOKE ALL ON TABLE seen_mac FROM postgres; +GRANT ALL ON TABLE seen_mac TO postgres; +GRANT SELECT,INSERT ON TABLE seen_mac TO nms; + + +-- +-- Name: squeue; Type: ACL; Schema: public; Owner: nms +-- + +REVOKE ALL ON TABLE squeue FROM PUBLIC; +REVOKE ALL ON TABLE squeue FROM nms; +GRANT ALL ON TABLE squeue TO nms; + + +-- +-- Name: switches; Type: ACL; Schema: public; Owner: nms +-- + +REVOKE ALL ON TABLE switches FROM PUBLIC; +REVOKE ALL ON TABLE switches FROM nms; +GRANT ALL ON TABLE switches TO nms; + + +-- +-- PostgreSQL database dump complete +-- + diff --git a/web/etc/varnish/default.vcl b/web/etc/varnish/default.vcl index 5f825a6..b4445d1 100644 --- a/web/etc/varnish/default.vcl +++ b/web/etc/varnish/default.vcl @@ -95,7 +95,7 @@ sub vcl_backend_response { set beresp.ttl = 0s; } if(bereq.url ~ "port-state.pl" && beresp.status == 200) { - set beresp.ttl = 30s; + set beresp.ttl = 1s; } if (beresp.status == 200 && bereq.url ~ "now=") { set beresp.ttl = 60m; diff --git a/web/nms.gathering.org/nms2/img/tg15-salkart-clean-big.png b/web/nms.gathering.org/nms2/img/tg15-salkart-clean-big.png Binary files differnew file mode 100644 index 0000000..8d647a3 --- /dev/null +++ b/web/nms.gathering.org/nms2/img/tg15-salkart-clean-big.png diff --git a/web/nms.gathering.org/nms2/index.html b/web/nms.gathering.org/nms2/index.html index 42d7a35..304cece 100644 --- a/web/nms.gathering.org/nms2/index.html +++ b/web/nms.gathering.org/nms2/index.html @@ -21,91 +21,109 @@ <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> - <![endif]--> - <style type="text/css"> - canvas { - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - outline: none; - -webkit-tap-highlight-color: rgba(255, 255, 255, 0); /* mobile webkit */ - } - </style> + <![endif]--> + <style type="text/css"> + canvas { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + outline: none; + -webkit-tap-highlight-color: rgba(255, 255, 255, 0); /* mobile webkit */ + } + </style> </head> -<body id="body"> + <body id="body"> <nav class="navbar navbar-default navbar-static-top"> <div class="container-fluid"> - <div class="navbar-header"> - <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> - <span class="sr-only">Toggle navigation</span> - <span class="icon-bar"></span> - <span class="icon-bar"></span> - <span class="icon-bar"></span> - </button> - <a class="navbar-brand" href="#">NMS</a> - </div> - <div id="navbar" class="navbar-collapse collapse"> - <ul class="nav navbar-nav"> - <li class="dropdown"> - <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Map mode<span class="caret"></span></a> - <ul class="dropdown-menu" role="menu"> - <li><a href="#ping" onclick="setUpdater(handler_ping)">Ping map</a></li> - <li><a href="#uplink" onclick="setUpdater(handler_uplinks)">Uplink map</a></li> - <li><a href="#temp" onclick="setUpdater(handler_temp)">Temperature map</a></li> - <li><a href="#traffic" onclick="setUpdater(handler_traffic)">Traffic map</a></li> - <li><a href="#comment" onclick="setUpdater(handler_comment)">Comment spotter</a></li> - <li><a href="#disco" onclick="setUpdater(handler_disco)">DISCO</a></li> - <li class="divider"> </li> - <li><a href="#" onclick="toggleNightMode()" title="Add 'nightMode' anywhere in the url to auto-enable">Toggle Night Mode</a></li> - <li><a href="#" onclick='showLayer("layerVisibility");'>Set layer visibility</a></li> - <li class="divider"> </li> - <li><a href="#" onclick="document.getElementById('nowPickerBox').style.display = 'block';">Travel in time</a></li> - <li><a href="#" onclick="startReplay();" title="Replay from opening 30 minutes per second">Replay TG</a></li> - <li><a href="#" onclick="document.getElementById('aboutData').style.display = 'block';">About TG15 data</a></li> - <li class="divider"> </li> - <li class="dropdown-header">Map scale</li> - <li><input type="range" id="scaler" name="points" min="0.2" max="3" step="0.01" onchange="scaleChange()" /></li> - <li><a href="#">Scale: <div id="scaler-text"></div></a></li> - <li class="divider"> </li> - <li><a onclick="document.getElementById('aboutBox').style.display = 'block'; hideSwitch();" style="cursor: pointer;" >About</a></li> - <li><a onclick="showTimerDebug(); hideSwitch();" style="cursor: pointer;" >Debug timers</a></li> - </ul> - </li> - <li><p id="updater_name" class="navbar-text"></p></li> - <div class="navbar-form navbar-left"> - <div class="form-group"> - <button class="btn btn-default" id="legend-1"></button> - <button class="btn btn-default" id="legend-2"></button> - <button class="btn btn-default" id="legend-3"></button> - <button class="btn btn-default" id="legend-4"></button> - <button class="btn btn-default" id="legend-5"></button> - </div> - </div> - </li> - </ul> - <ul class="nav navbar-nav navbar-right"> - <li><p id="speed" class="navbar-text" title="Client port speed / Total port speed"></p></li> - </ul> - </div><!--/.nav-collapse --> + <div class="navbar-header"> + <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> + <span class="sr-only">Toggle navigation</span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </button> + <a class="navbar-brand" onclick="toggleLayer('aboutBox'); //document.getElementById('aboutBox').style.display = 'block'; hideSwitch();" style="cursor: pointer;" >NMS</a> + </div> + <div id="navbar" class="navbar-collapse collapse"> + <ul class="nav navbar-nav"> + <li class="dropdown"> + <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Map mode + <span class="caret"></span> + </a> + <ul class="dropdown-menu" role="menu"> + <li><a href="#ping" onclick="setUpdater(handler_ping)">Ping map</a></li> + <li><a href="#uplink" onclick="setUpdater(handler_uplinks)">Uplink map</a></li> + <li><a href="#temp" onclick="setUpdater(handler_temp)">Temperature map</a></li> + <li><a href="#traffic" onclick="setUpdater(handler_traffic)">Traffic map</a></li> + <li><a href="#comment" onclick="setUpdater(handler_comment)">Comment spotter</a></li> + <li><a href="#traffictot" onclick="setUpdater(handler_traffic_tot)">Total switch traffic</a></li> + <li><a href="#disco" onclick="setUpdater(handler_disco)">DISCO</a></li> + <li class="divider"> </li> + <li><a href="#" onclick="toggleLayer('nowPickerBox');document.getElementById('nowPicker').focus();">Travel in time</a></li> + <li><a href="#" onclick="startReplay();" title="Replay from opening 120 minutes per second">Replay TG</a></li> + <li class="divider"> </li> + <li><a onclick="showTimerDebug(); hideSwitch();" style="cursor: pointer;" >Debug timers</a></li> + </ul> + </li> + <li class="dropdown"> + <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">View<span class="caret"></span></a> + <ul class="dropdown-menu" role="menu"> + <li><a href="#" onclick="toggleNightMode()">Toggle Night Mode</a></li> + <li><a href="#" onclick="showBlurBox()">Tweak Night Mode blur</a></li> + <li><a href="#" onclick='showLayer("layerVisibility");'>Set layer visibility</a></li> + <li class="divider"> </li> + <li class="dropdown-header">Map scale</li> + <li><a href="#"><label id="scaler-text" for='scaler'></label><input type="range" id="scaler" name="points" min="0.2" max="3" step="0.01" onchange="scaleChange()" /></a></li> + </ul> + </li> + <li><p id="updater_name" class="navbar-text"></p></li> + <div class="navbar-form navbar-left"> + <div class="form-group"> + <button class="btn btn-default" id="legend-1"></button> + <button class="btn btn-default" id="legend-2"></button> + <button class="btn btn-default" id="legend-3"></button> + <button class="btn btn-default" id="legend-4"></button> + <button class="btn btn-default" id="legend-5"></button> + </div> + </div> + </li> + </ul> + <ul class="nav navbar-nav navbar-right"> + <li><p id="speed" class="navbar-text" title="Client port speed / Total port speed"></p></li> + <li class="dropdown"> + <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Help + <span class="caret"></span> + </a> + <ul class="dropdown-menu" role="menu"> + <li><a href="#" onclick="toggleLayer('aboutData');">About TG15 data</a></li> + <li><a href="#" onclick="toggleLayer('aboutBox');" >About NMS</a></li> + <li><a href="#" onclick="toggleLayer('aboutPerformance');" >About Performance</a></li> + <li><a href="#" onclick="toggleLayer('aboutKeybindings');" >Keyboard Shortcuts</a></li> + </ul> + </li> + </ul> + </div><!--/.nav-collapse --> </div> </nav> <div class="container-fluid"> - <div class="row-fluid"> - <div class="span12"> - <div id="aboutData" class="col-md-4" - style="position: absolute; display:none; z-index: 130;"> - <div id="abotData" class="panel panel-default"> - <div class="panel-heading"><h3 class="panel-title">About - the TG15 data - <button type="button" class="close" aria-label="Close" onclick="document.getElementById('aboutData').style.display = 'none';" style="float: right"><span - aria-hidden="true">×</span></button></h3></div> - <div class="panel-body"> + <div class="row-fluid"> + <div class="span12"> + <div id="aboutData" class="col-md-4" style="position: absolute; display:none; z-index: 130;"> + <div id="abotData" class="panel panel-default"> + <div class="panel-heading"> + <h3 class="panel-title">About the TG15 data + <button type="button" class="close" aria-label="Close" onclick="document.getElementById('aboutData').style.display = 'none';" style="float: right"> + <span aria-hidden="true">×</span> + </button> + </h3> + </div> + <div class="panel-body"> <p>The data you see from The Gathering 2015 will seem "broken up". This is not because we don't have data from the first day, but because the backend was re-written on @@ -130,147 +148,374 @@ <p>It is also worth mentioning that things like switch positions are not logged historically, so you see the final position on the map.</p> + </div> + </div> + </div> + <div id="aboutPerformance" class="col-md-4" style="position: absolute; display:none; z-index: 130;"> + <div class="panel panel-default"> + <div class="panel-heading"> + <h3 class="panel-title">Performance + <button type="button" class="close" aria-label="Close" onclick="document.getElementById('aboutPerformance').style.display = 'none';" style="float: right"> + <span aria-hidden="true">×</span> + </button> + </h3> + </div> + <table class="table"> + <tr> + <td>Outstanding AJAX requests</td> + <td id="outstandingAJAX"></td> + </tr> + <tr> + <td>Overflowed AJAX requests</td> + <td id="overflowAJAX"></td> + </tr> + </table> + <div class="panel-body"> + <p>NMS performance is surprisingly complex. It's split into + several parts and dealt with differently.</p> + <p>Poller performance is a matter of efficiently collecting + data and is mostly handled in the Perl code (and ensuring + we use sensible database schemas).</p> + <p>Backend performance for the GUI is mostly about not + killing the database server. We do NOT try to protect + against malicious clients directly, since this is a + management system not public-facing, but Varnish is used to + cache requests. To be able to do that properly, we need use + absolute time when reviewing past events (so "2015-04-02 + 17:30:00", not "2 hours ago"). We've also tried to minimize + the stupidity in the queries. There's still work to be done + here, though, as we need to split up a few large backend + requests (port-state.pl).</p> + <p>Front-end performance is mostly about drawing things + sensibly and not completely bombing the memory usage. And + about gracefully handling slow backends This will affect + you. For example, if you are reviewing past events and the + DB is struggling, we'll simply skip a backend request if we + have too many outstanding requests, that means you may jump + from "17:00" to "18:30" instead of going through + "17:30" and "18:00" too. This is working as intended. It + also means that you can happily spam the forward/backward + keyboard bindings to jump 18 hours forward: You'll overflow + the extra AJAX requests for individual requests, but you'll + land at the right time when you let go. But there could be + a 1 second delay (or more if the backend really struggles) + since you'll have to rely on the periodic backend requests + instead of the explicit ones triggered on hitting a + button.</p> + <p>Note that the counters on top are updated on a timer, + but this timer is set up at the same time as everything + else, which means that it's likely to update at the same + time as we fire off AJAX requests, so the 'outstanding ajax + requests' counter might either show almost constantly 3 or + 0 depending on what timer happens to fire first. This does + NOT mean that NMS has 3 requests all the time, just that + we're checking right after we fire off AJAX requests every + time.</p> + <p>NMS also tries to handle drawing OK, which is why things + are split into different HTML5 canvases. Blur and text are + particularly expensive, but there's no reason to re-paint + that all the time, etc).</p> + <p>The basic performance experiments are done on TG15 data + using a laptop and a VM with 6GB of memory, so it should + hold up quite well on "proper" hardware.</p> + </div> + </div> + </div> + <div id="aboutKeybindings" class="col-md-4" style="position: absolute; display:none; z-index: 130;"> + <div class="panel panel-default"> + <div class="panel-heading"> + <h3 class="panel-title">Keyboard Shortcuts + <button type="button" class="close" aria-label="Close" onclick="document.getElementById('aboutKeybindings').style.display = 'none';" style="float: right"> + <span aria-hidden="true">×</span> + </button> + </h3> + </div> + <table class="table table-condensed"> + <tr> + <th>Key</th> + <th>Description</th> + </tr> + <tr> + <td>?</td> + <td>Toggle navigation bar</td> + </tr> + <tr> + <td>n</td> + <td>Toggle night mode</td> + </tr> + <tr> + <td>1</td> + <td>View Ping map</td> + </tr> + <tr> + <td>2</td> + <td>View uplink map</td> + </tr> + <tr> + <td>3</td> + <td>View temperature map</td> + </tr> + <tr> + <td>4</td> + <td>View uplink traffic map</td> + </tr> + <tr> + <td>5</td> + <td>View comment spotter map</td> + </tr> + <tr> + <td>6</td> + <td>View total switch traffic map</td> + </tr> + <tr> + <td>7</td> + <td>View Disco map</td> + </tr> + <tr> + <td>h</td> + <td>Step 1 hour back in time</td> + </tr> + <tr> + <td>j</td> + <td>Step 5 minutes back in time</td> + </tr> + <tr> + <td>k</td> + <td>Step 5 minutes forward in time</td> + </tr> + <tr> + <td>l</td> + <td>Step 1 hour forward in time</td> + </tr> + <tr> + <td>p</td> + <td>Toggle playback (1 hour per second)</td> + </tr> + <tr> + <td>r</td> + <td>Return to real time</td> + </tr> + </table> + </div> + </div> + <div id="nowPickerBox" style="position: absolute; display: none; z-index: 130;" class="col-md-4"> + <div class="panel panel-default"> + <div class="panel-heading"> + <h3 class="panel-title">Time travel + <button type="button" class="close" aria-labe="Close" onclick="document.getElementById('nowPickerBox').style.display = 'none';" style="float: right;"> + <span aria-hidden="true">×</span> + </button> + </h3> + </div> + <div class="panel-body"> + <p>Some features do not have time travel support (comment + spotting and DHCP map at the moment). We also lack + compatible SNMP data for the first day or so, so you'll + only have ping data for the first day of TG15.</p> + <p>It could take some time to load a specific point in time + for the first time. See "About performance" under the help + menu for more information.</p> + <p>You can also step backwards and forwards in time, stop + and start replay and go back to real time using keyboard + shortcuts. See the help menu for an overview of keyboard + shortcuts.</p> + <div class="input-group"> + <input type="text" class="form-control" placeholder="YYYY-MM-DD hh:mm:ss" id="nowPicker"> + <span class="input-group-btn"> + <button class="btn btn-default" onclick="changeNow();">Travel</button> + </span> </div> - - </div> - </div> - <div id="nowPickerBox" class="panel panel-default" style="position: absolute; display:none; z-index: 130;" > - <div class="panel-heading"><h3 - class="panel-title">Time travel</h3></div> - <div class="panel-body"> - <input type="text" class="form-control" placeholder="YYYY-MM-DD hh:mm:ss" id="nowPicker" value="" /> - <button class="btn btn-default" onclick="changeNow();">Travel</button> - <button class="btn" onclick="document.getElementById('nowPickerBox').style.display = 'none';">Cancel</button> - </div> - </div> - <div style="position: absolute; z-index: 120;" class="col-md-3"> - <div id="info-switch-parent" class="panel panel-default col-d-6" style="display: none; backgroun:silver; position: absolute; z-index: 120;"> - <div class="panel-heading"><h3 class="panel-title" - id="info-switch-title"></h3></div> - <div id="info-switch-panel-body"> - <table class="table" id="info-switch-table"></table> - </div> - </div> - </div> - <div id="aboutBox" class="col-md-4" style="display: none; - position: absolute; z-index: 100;"> + </div> + </div> + </div> + <div style="position: absolute; z-index: 120;" class="col-md-4"> + <div id="info-switch-parent" class="panel panel-default col-d-6" style="display: none; backgroun:silver; position: absolute; z-index: 120;"> + <div class="panel-heading"> + <h3 class="panel-title" id="info-switch-title"></h3> + </div> + <div id="info-switch-panel-body"> + <table class="table" id="info-switch-table"></table> + </div> + </div> + </div> + <div id="aboutBox" class="col-md-4" style="display: none; position: absolute; z-index: 100;"> <div id="abotBox" class="panel panel-default"> - <div class="panel-heading"><h3 class="panel-title">Welcome to NMS - <button type="button" class="close" aria-labe="Close" onclick="document.getElementById('aboutBox').style.display = 'none';" style="float: right;"><span aria-hidden="true">×</span></button></h3></div> - <div class="panel-body"> - - <h3>Cool stuff:</h3> - <ul> - <li>Click a switch for more info</li> - <li>Rewind: You can check out state at a specific time or replay from the beginning of the event. Only works for data where we keep time-series (so not for comments)</li> - <li>Night mode, now with blur. Optional disco-mode (that's - mainly for testing, though).</li> - <li>Auto-scaling the viewport/canvas</li> - <li>Total client speed (up right)</li> - <li>Generic(-ish) map handlers: provide a name, init-function - and an update-function and the nms lib does the rest as far as - integration goes.</li> - </ul> - <h3>Todo list front end:</h3> - <ul> - <li>Polish time travel UI (Allow playing from a given time at a given speed, play/pause buttons, etc)</li> - <li>Toggle auto-scale on/off</li> - <li>Clean up various global variables</li> - <li>Split blur into separate canvas (canvas is there, it's just - not used)</li> - <li>Add DHCP map</li> - <li>Add magic map (combined map of sorts)</li> - <li>Adjust updatePorts() frequency based on necessity (1sec - updates is overkill for regular operation, but needed for time - travel)</li> - <li>More info on switches: Port state, possibly link time - trends</li> - <li>Moving switches around (like ping.html + edit)</li> - <li>Split nms.js into multiple components to unclutter the - code</li> - <li>Comments: Fix UTF8 garbligash caused by $dbh->quote()</li> - <li>More.</li> - </ul> - <h3>Todo for backend:</h3> - <ul> - <li>IPv6 support</li> - <li>Close SQL injections (IT'S WIDE OPEN BECAUSE WHY NOT THAT'S NEVER A PROBLEM)</li> - <li>Split port-state.pl into multiple appropriate pieces. Right - it mixes heavy time-critical data with less time-critical and - cheap computation.</li> - <li>Consider time log of DHCP (right now it just stores the - most recent timestamp, making time travel impossible)</li> - <li>Fix SNMP-fetcher so it gets ifXTable and at least ifOperStatus from ifTable. Don't request the entire ifXTable if we can avoid it. Possibly other tweaks.</li> - <li>Support for adding switches through an API, not just pure SQL.</li> - <li>Integrate with FAP</li> - <li>Clean up old interfaces</li> - <li>Review various agents/tools</li> - <li>Improve cache headers</li> - <li>Cache invalidation of comments?</li> - <li>Re-test the SQL schema. It's been modified and works fine - on my laptop, but I need to dump it, commit it and test it.</li> - <li>Munin plugin for ports.</li> - </ul> + <div class="panel-heading"> + <h3 class="panel-title">Welcome to NMS + <button type="button" class="close" aria-labe="Close" onclick="document.getElementById('aboutBox').style.display = 'none';" style="float: right;"> + <span aria-hidden="true">×</span> + </button> + </h3> + </div> + <div class="panel-body"> + <h3>Cool stuff:</h3> + <ul> + <li>Click a switch for more info</li> + <li>Rewind: You can check out state at a specific time or + replay from the beginning of the event. Only works for + data where we keep time-series (so not for + comments)</li> + <li>Press '?' to toggle the menu.</li> + <li>Auto-scaling the viewport/canvas</li> + <li>Total client speed (up right)</li> + <li>Generic(-ish) map handlers: provide a name, init-function + and an update-function and the nms lib does the rest as far as + integration goes.</li> + </ul> + <h3>Todo list front end:</h3> + <ul> + <li>Polish time travel UI (Allow playing from a given time at a given speed, play/pause buttons, etc)</li> + <li>Better "popup" boxes: It's growing out of control.</li> + <li>Toggle auto-scale on/off</li> + <li>Clean up various global variables</li> + <li>Create name spaces in nms.*: It's just barely better + than global stuff now.</li> + <li>Add DHCP map</li> + <li>More info on switches: Port state, possibly link time + trends</li> + <li>Moving switches around (like ping.html + edit)</li> + <li>Split nms.js into multiple components to unclutter the + code</li> + <li>Comments: Fix UTF8 garbligash caused by $dbh->quote()</li> + </ul> + <h3>Todo for backend:</h3> + <ul> + <li>IPv6 support</li> + <li>Provide public API's</li> + <li>Investigate a json tree filter/massager</li> + <li>Close SQL injections (IT'S WIDE OPEN BECAUSE WHY NOT THAT'S NEVER A PROBLEM)</li> + <li>Split port-state.pl into multiple appropriate pieces. Right + it mixes heavy time-critical data with less time-critical and + cheap computation.</li> + <li>Rip comments out of port-state.pl completely so it's not + bound by the same cache issues and can be reliably + refreshed.</li> + <li>Consider time log of DHCP (right now it just stores the + most recent timestamp, making time travel impossible)</li> + <li>Fix SNMP-fetcher so it gets ifXTable and at least + ifOperStatus from ifTable. Don't request the entire + ifXTable if we can avoid it. Possibly other + tweaks.</li> + <li>Support for adding switches through an API, not just pure SQL.</li> + <li>Integrate with FAP</li> + <li>Clean up old interfaces</li> + <li>Review various agents/tools</li> + <li>Improve cache headers</li> + <li>Cache invalidation of comments? (Probably not needed)</li> + <li>Re-test the SQL schema. It's been modified and works fine + on my laptop, but I need to dump it, commit it and test it.</li> + <li>Munin plugin for ports.</li> + </ul> + </div> </div> - + </div> + <div id="blurManic" class="panel panel-default" style="display: none; position: absolute; z-index: 100;"> + <div class="panel-heading"> + <h1 class="panel-title">Blur tweaks + <button type="button" class="close" aria-labe="Close" onclick="document.getElementById('blurManic').style.display = 'none';" style="float: right;"> + <span aria-hidden="true">×</span> + </button> + </h1> </div> + <div class="panel-body"> + <div class="form"> + <div class="form-group"> + <label for="shadowBlur">Blur strength</label> + <input type="number" id="shadowBlur" class="form-control"> + </div> + <div class="form-group"> + <label for="shadowColor">Blur color</label> + <input type="color" id="shadowColor" class="form-control"> + </div> + <button type="button" class="btn btn-default" onclick="applyBlur();">Apply</button> + </div> </div> - <div id="debugTimers" class="panel panel-default" style="display: none; position: absolute; z-index: 100;"> - <div class="panel-heading"><h1 class="panel-title">Debug - timers (e.g.: Break stuff! FAST!) - <button type="button" class="close" aria-labe="Close" onclick="document.getElementById('debugTimers').style.display = 'none';" style="float: right;"><span aria-hidden="true">×</span></button></h1></div> - <div id="timerTableTop" class="panel-body"> + </div> + </div> + <div id="debugTimers" class="panel panel-default" style="display: none; position: absolute; z-index: 100;"> + <div class="panel-heading"> + <h1 class="panel-title">Debug timers (e.g.: Break stuff! FAST!) + <button type="button" class="close" aria-labe="Close" onclick="document.getElementById('debugTimers').style.display = 'none';" style="float: right;"> + <span aria-hidden="true">×</span> + </button> + </h1> + </div> + <div id="timerTableTop" class="panel-body"> <p>These are internal timers for the NMS frontend. They are provided mainly to debug the frontend. Setting AJAX-triggering counters to ridiculous numbers is not advised (mainly because it causes server load).</p> - <table id="timerTable"> </table> - </div> - </div> - <div id="layerVisibility" class="panel panel-default" style="display: none; position: absolute; z-index: 100;"> - <div class="panel-heading"><h1 class="panel-title">Set layer visibility - <button type="button" class="close" aria-labe="Close" onclick="document.getElementById('layerVisibility').style.display = 'none';" style="float: right;"><span aria-hidden="true">×</span></button></h1></div> - <div class="panel-body"> + </div> + <table id="timerTable"> </table> + </div> + <div id="layerVisibility" class="panel panel-default" style="display: none; position: absolute; z-index: 100;"> + <div class="panel-heading"> + <h1 class="panel-title">Set layer visibility + <button type="button" class="close" aria-labe="Close" onclick="document.getElementById('layerVisibility').style.display = 'none';" style="float: right;"> + <span aria-hidden="true">×</span> + </button> + </h1> + </div> + <div class="panel-body"> <table id="visibilityTable" class="table"> - <tr><td>Background</td><td> - <button onclick='hideLayer("bgCanvas");'>Hide</button> - <button onclick='showLayer("bgCanvas");'>Show</button> - </td></tr> - <tr><td>Linknets</td><td> - <button onclick='hideLayer("linkCanvas");'>Hide</button> - <button onclick='showLayer("linkCanvas");'>Show</button> - </td></tr> - <tr><td>Blur</td><td> - <button onclick='hideLayer("blurCanvas");'>Hide</button> - <button onclick='showLayer("blurCanvas");'>Show</button> - </td></tr> - <tr><td>Switches</td><td> - <button onclick='hideLayer("switchCanvas");'>Hide</button> - <button onclick='showLayer("switchCanvas");'>Show</button> - </td></tr> - <tr><td>Text</td><td> - <button onclick='hideLayer("textCanvas");'>Hide</button> - <button onclick='showLayer("textCanvas");'>Show</button> - </td></tr> - <tr><td>Top</td><td> - <button onclick='hideLayer("topCanvas");'>Hide</button> - <button onclick='showLayer("topCanvas");'>Show</button> - </td></tr> - </table> - </div> - </div> + <tr> + <td>Background</td> + <td> + <button onclick='hideLayer("bgCanvas");'>Hide</button> + <button onclick='showLayer("bgCanvas");'>Show</button> + </td> + </tr> + <tr> + <td>Linknets</td> + <td> + <button onclick='hideLayer("linkCanvas");'>Hide</button> + <button onclick='showLayer("linkCanvas");'>Show</button> + </td> + </tr> + <tr> + <td>Blur</td> + <td> + <button onclick='hideLayer("blurCanvas");'>Hide</button> + <button onclick='showLayer("blurCanvas");'>Show</button> + </td> + </tr> + <tr> + <td>Switches</td> + <td> + <button onclick='hideLayer("switchCanvas");'>Hide</button> + <button onclick='showLayer("switchCanvas");'>Show</button> + </td> + </tr> + <tr> + <td>Text</td> + <td> + <button onclick='hideLayer("textCanvas");'>Hide</button> + <button onclick='showLayer("textCanvas");'>Show</button> + </td> + </tr> + <tr> + <td>Timestamp</td> + <td> + <button onclick='hideLayer("topCanvas");'>Hide</button> + <button onclick='showLayer("topCanvas");'>Show</button> + </td> + </tr> + </table> + </div> + </div> -<canvas id="bgCanvas" width="1920" height="1032" style="position: absolute; z-index: 1;"> </canvas> -<canvas id="linkCanvas" width="1920" height="1032" style="position: absolute; z-index: 10;"> </canvas> -<canvas id="blurCanvas" width="1920" height="1032" style="position: absolute; z-index: 20;"> </canvas> -<canvas id="switchCanvas" width="1920" height="1032" style="position: absolute; z-index: 30;"> </canvas> -<canvas id="textCanvas" width="1920" height="1032" style="position: absolute; z-index: 40;"> </canvas> -<canvas id="topCanvas" width="1920" height="1032" style="position: absolute; z-index: 50; cursor: pointer;" onclick="canvasClick(event)"> -</canvas> + <canvas id="bgCanvas" width="1920" height="1032" style="position: absolute; z-index: 1;"> </canvas> + <canvas id="linkCanvas" width="1920" height="1032" style="position: absolute; z-index: 10;"> </canvas> + <canvas id="blurCanvas" width="1920" height="1032" style="position: absolute; z-index: 20;"> </canvas> + <canvas id="switchCanvas" width="1920" height="1032" style="position: absolute; z-index: 30;"> </canvas> + <canvas id="textCanvas" width="1920" height="1032" style="position: absolute; z-index: 40;"> </canvas> + <canvas id="topCanvas" width="1920" height="1032" style="position: absolute; z-index: 50;"> </canvas> + <canvas id="inputCanvas" width="1920" height="1032" style="position: absolute; z-index: 60; cursor: pointer;" onclick="canvasClick(event)"> + </canvas> + <canvas id="hiddenCanvas" width="1000" height="10" style="display: none; position: absolute; z-index: 1000 "></canvas> -<div style="display:none;"><img id="source" src="img/tg15-salkart-full.png" ></div> - </div> - </div> + <div style="display:none;"><img id="source" src="img/tg15-salkart-clean-big.png" ></div> + </div> </div><!--/.fluid-container--> <script src="js/jquery.min.js" type="text/javascript"></script> <script src="js/bootstrap.min.js" type="text/javascript"></script> @@ -278,7 +523,7 @@ <script type="text/javascript" src="js/nms-color-util.js"></script> <script type="text/javascript" src="js/nms-map-handlers.js"></script> <script type="text/javascript"> - initNMS(); +initNMS(); </script> -</body> + </body> </html> diff --git a/web/nms.gathering.org/nms2/js/nms-color-util.js b/web/nms.gathering.org/nms2/js/nms-color-util.js index 28f7e1b..72d6f61 100644 --- a/web/nms.gathering.org/nms2/js/nms-color-util.js +++ b/web/nms.gathering.org/nms2/js/nms-color-util.js @@ -1,54 +1,91 @@ +/* + * Some stolen colors that look OK. + * + * PS: Stolen from boostrap, because we use bootstrap and these look good + * and match. + */ +var lightblue = "#d9edf7"; +var lightgreen = "#dff0d8"; +var lightred = "#f2dede"; +var lightorange = "#fcf8e3"; +var blue = "#337ab7"; +var green = "#5cb85c"; +var teal = "#5bc0de"; // Or whatever the hell that is +var orange = "#f0ad4e"; +var red = "#d9534f"; + function gradient_from_latency(latency_ms, latency_secondary_ms) { - if (latency_secondary_ms === undefined) { - return rgb_from_latency(latency_ms); - } - return 'linear-gradient(' + - rgb_from_latency(latency_ms) + ', ' + - rgb_from_latency(latency_secondary_ms) + ')'; + if (latency_ms == undefined) + return blue; + return getColorStop(parseInt(latency_ms) * 10); } -function rgb_from_latency(latency_ms) +/* + * Return a random-ish color (for testing) + */ +function getRandomColor() { - if (latency_ms === null || latency_ms === undefined) { - return '#0000ff'; - } - - var l = latency_ms / 40.0; - if (l >= 2.0) { - return 'rgb(255, 0, 0)'; - } else if (l >= 1.0) { - l = 2.0 - l; - l = Math.pow(l, 1.0/2.2); - l = Math.floor(l * 255.0); - return 'rgb(255, ' + l + ', 0)'; - } else { - l = Math.pow(l, 1.0/2.2); - l = Math.floor(l * 255.0); - return 'rgb(' + l + ', 255, 0)'; - } + var colors = [ "white", red, teal, orange, green, blue ]; + var i = Math.round(Math.random() * (colors.length-1)); + return colors[i]; } /* - * Give us a color from blue (0) to red (100). + * Set up the hidden gradient canvas, using an array as input. + * + * This gives us a flexible way to get gradients between any number of + * colors (green to red, or blue to green to orange to red to white to pink + * to black and so on). + * + * Typically called when setting up a map handler. Currently "single + * tenant", since there's just one canvas. + * + * XXX: We have to store the gradients in nms.* and restore this when we + * resize for the moment, because this canvas is also re-sized (which isn't + * really necessary, but avoids special handling). */ -function rgb_from_max(x) +function drawGradient(gradients) { - x = x/100; - var colorred = 255 * x; - var colorblue = 255 - colorred; - - return 'rgb(' + Math.floor(colorred) + ", 0, " + Math.floor(colorblue) + ')'; + var gradient = dr.hidden.ctx.createLinearGradient(0,0,1000,0); + var stops = gradients.length - 1; + nms.gradients = gradients; + for (var color in gradients) { + var i = color / stops; + gradient.addColorStop(i, gradients[color]); + } + dr.hidden.ctx.beginPath(); + dr.hidden.ctx.strokeStyle = gradient; + dr.hidden.ctx.moveTo(0,0); + dr.hidden.ctx.lineTo(1000,0); + dr.hidden.ctx.lineWidth = 10; + dr.hidden.ctx.closePath(); + dr.hidden.ctx.stroke(); + dr.hidden.ctx.moveTo(0,0); } /* - * Return a random-ish color (for testing) + * Get the color of a gradient, range is from 0 to 999 (inclusive). */ -function getRandomColor() -{ - var i = Math.round(Math.random() * 5); - var colors = [ "white", "red", "pink", "yellow", "orange", "green" ]; - return colors[i]; +function getColorStop(x) { + x = parseInt(x); + if (x > 999) + x = 999; + if (x < 0) + x = 0; + return getColor(x,0); } +/* + * Get the color on the hidden canvas at a specific point. Could easily be + * made generic. + */ +function getColor(x,y) { + var imageData = dr.hidden.ctx.getImageData(x, y, 1, 1); + var data = imageData.data; + if (data.length < 4) + return false; + var ret = 'rgb(' + data[0] + ',' + data[1] + ',' + data[2] + ')'; + return ret; +} diff --git a/web/nms.gathering.org/nms2/js/nms-map-handlers.js b/web/nms.gathering.org/nms2/js/nms-map-handlers.js index 762788a..9b2d44c 100644 --- a/web/nms.gathering.org/nms2/js/nms-map-handlers.js +++ b/web/nms.gathering.org/nms2/js/nms-map-handlers.js @@ -21,38 +21,62 @@ var handler_uplinks = { updater:uplinkUpdater, init:uplinkInit, + tag:"uplink", name:"Uplink map" }; var handler_temp = { updater:tempUpdater, init:tempInit, + tag:"temp", name:"Temperature map" }; var handler_ping = { updater:pingUpdater, init:pingInit, + tag:"ping", name:"IPv4 Ping map" }; var handler_traffic = { updater:trafficUpdater, init:trafficInit, + tag:"traffic", name:"Uplink traffic map" }; +var handler_traffic_tot = { + updater:trafficTotUpdater, + init:trafficTotInit, + tag:"traffictot", + name:"Switch traffic map" +}; + var handler_disco = { updater:randomizeColors, init:discoInit, + tag:"disco", name:"Disco fever" }; var handler_comment = { updater:commentUpdater, init:commentInit, + tag:"comment", name:"Fresh comment spotter" }; + +var handlers = [ + handler_uplinks, + handler_temp, + handler_ping, + handler_traffic, + handler_disco, + handler_comment, + handler_traffic_tot + ]; + /* * Update function for uplink map * Run periodically when uplink map is active. @@ -78,15 +102,15 @@ function uplinkUpdater() } } if (uplinks == 0) { - setSwitchColor(sw,"blue"); + setSwitchColor(sw,"white"); } else if (uplinks == 1) { - setSwitchColor(sw,"red"); + setSwitchColor(sw,red); } else if (uplinks == 2) { - setSwitchColor(sw, "yellow"); + setSwitchColor(sw, orange); } else if (uplinks == 3) { - setSwitchColor(sw, "green"); + setSwitchColor(sw, green); } else if (uplinks > 3) { - setSwitchColor(sw, "white"); + setSwitchColor(sw, blue); } } } @@ -94,13 +118,27 @@ function uplinkUpdater() /* * Init-function for uplink map */ +function uplinkInit() +{ + setLegend(1,"white","0 uplinks"); + setLegend(2,red,"1 uplink"); + setLegend(3,orange,"2 uplinks"); + setLegend(4,green,"3 uplinks"); + setLegend(5,blue,"4 uplinks"); +} + +/* + * Init-function for uplink map + */ function trafficInit() { - setLegend(1,"blue","0 (N/A)"); - setLegend(5,"red", "1000Mb/s or more"); - setLegend(4,"yellow","100Mb/s to 800Mb/s"); - setLegend(3,"green", "5Mb/s to 100Mb/s"); - setLegend(2,"white","0 to 5Mb/s"); + var m = 1024 * 1024 / 8; + drawGradient([lightgreen,green,orange,red]); + setLegend(1,colorFromSpeed(0),"0 (N/A)"); + setLegend(5,colorFromSpeed(2000 * m) , "2000Mb/s"); + setLegend(4,colorFromSpeed(1500 * m),"1500Mb/s"); + setLegend(3,colorFromSpeed(500 * m),"500Mb/s"); + setLegend(2,colorFromSpeed(10 * m),"10Mb/s"); } function trafficUpdater() @@ -115,37 +153,62 @@ function trafficUpdater() /ge-0\/0\/46$/.exec(port) || /ge-0\/0\/47$/.exec(port)) { + if (!nms.switches_then["switches"][sw] || + !nms.switches_then["switches"][sw]["ports"] || + !nms.switches_then["switches"][sw]["ports"][port]) + continue; var t = nms.switches_then["switches"][sw]["ports"][port]; var n = nms.switches_now["switches"][sw]["ports"][port]; speed += (parseInt(t["ifhcoutoctets"]) -parseInt(n["ifhcoutoctets"])) / (parseInt(t["time"] - n["time"])); speed += (parseInt(t["ifhcinoctets"]) -parseInt(n["ifhcinoctets"])) / (parseInt(t["time"] - n["time"])); } } - var m = 1024 * 1024 / 8; - if (speed == 0) { - setSwitchColor(sw,"blue"); - } else if (speed > (1000 * m)) { - setSwitchColor(sw,"red"); - } else if (speed > (800 * m)) { - setSwitchColor(sw, "yellow"); - } else if (speed > (5 * m)) { - setSwitchColor(sw, "green"); - } else { - setSwitchColor(sw, "white"); - } + setSwitchColor(sw,colorFromSpeed(speed)); } } /* * Init-function for uplink map */ -function uplinkInit() +function trafficTotInit() { - setLegend(1,"blue","0 uplinks"); - setLegend(2,"red","1 uplink"); - setLegend(3,"yellow","2 uplinks"); - setLegend(4,"green","3 uplinks"); - setLegend(5,"white","4 uplinks"); + var m = 1024 * 1024 / 8; + drawGradient([lightgreen,green,orange,red]); + setLegend(1,colorFromSpeed(0),"0 (N/A)"); + setLegend(5,colorFromSpeed(5000 * m,5) , "5000Mb/s"); + setLegend(4,colorFromSpeed(3000 * m,5),"3000Mb/s"); + setLegend(3,colorFromSpeed(1000 * m,5),"1000Mb/s"); + setLegend(2,colorFromSpeed(100 * m,5),"100Mb/s"); +} + +function trafficTotUpdater() +{ + if (!nms.switches_now["switches"]) + return; + for (sw in nms.switches_now["switches"]) { + var speed = 0; + for (port in nms.switches_now["switches"][sw]["ports"]) { + if (!nms.switches_then["switches"][sw] || + !nms.switches_then["switches"][sw]["ports"] || + !nms.switches_then["switches"][sw]["ports"][port]) + continue; + var t = nms.switches_then["switches"][sw]["ports"][port]; + var n = nms.switches_now["switches"][sw]["ports"][port]; + speed += (parseInt(t["ifhcoutoctets"]) -parseInt(n["ifhcoutoctets"])) / (parseInt(t["time"] - n["time"])); + } + setSwitchColor(sw,colorFromSpeed(speed,5)); + } +} + +function colorFromSpeed(speed,factor) +{ + var m = 1024 * 1024 / 8; + if (factor == undefined) + factor = 2; + if (speed == 0) + return blue; + speed = speed < 0 ? 0 : speed; + return getColorStop( 1000 * (speed / (factor * (1000 * m)))); } @@ -158,11 +221,11 @@ function temp_color(t) { if (t == undefined) { console.log("Temp_color, but temp is undefined"); - return "blue"; + return blue; } - t = parseInt(t) - 20; - t = Math.floor((t / 15) * 100); - return rgb_from_max(t); + t = parseInt(t) - 12; + t = Math.floor((t / 23) * 1000); + return getColorStop(t); } function tempUpdater() @@ -179,23 +242,28 @@ function tempUpdater() function tempInit() { - setLegend(1,temp_color(20),"20 °C"); - setLegend(2,temp_color(22),"22 °C"); - setLegend(3,temp_color(27),"27 °C"); - setLegend(4,temp_color(31),"31 °C"); + drawGradient(["black",blue,lightblue,lightgreen,green,orange,red]); + setLegend(1,temp_color(15),"15 °C"); + setLegend(2,temp_color(20),"20 °C"); + setLegend(3,temp_color(25),"25 °C"); + setLegend(4,temp_color(30),"30 °C"); setLegend(5,temp_color(35),"35 °C"); } function pingUpdater() { + if (!nms.ping_data || !nms.ping_data["switches"]) { + resetColors(); + return; + } for (var sw in nms.switches_now["switches"]) { - var c = "blue"; + var c = blue; if (nms.ping_data['switches'] && nms.ping_data['switches'][sw]) c = gradient_from_latency(nms.ping_data["switches"][sw]["latency"]); setSwitchColor(sw, c); } for (var ln in nms.switches_now["linknets"]) { - var c1 = "blue"; + var c1 = blue; var c2 = c1; if (nms.ping_data['linknets'] && nms.ping_data['linknets'][ln]) { c1 = gradient_from_latency(nms.ping_data["linknets"][ln][0]); @@ -207,56 +275,58 @@ function pingUpdater() function pingInit() { + drawGradient([green,lightgreen,orange,red]); setLegend(1,gradient_from_latency(1),"1ms"); setLegend(2,gradient_from_latency(30),"30ms"); setLegend(3,gradient_from_latency(60),"60ms"); - setLegend(4,gradient_from_latency(80),"80ms"); - setLegend(5,"#0000ff" ,"No response"); + setLegend(4,gradient_from_latency(100),"100ms"); + setLegend(5,gradient_from_latency(undefined) ,"No response"); } function commentUpdater() { var realnow = Date.now(); - if (nms.now) { - realnow = Date.parse(nms.now); - } var now = Math.floor(realnow / 1000); for (var sw in nms.switches_now["switches"]) { - var c = "green"; + var c = "white"; var s = nms.switches_now["switches"][sw]; if (s["comments"] && s["comments"].length > 0) { var then = 0; + var active = 0; + var persist = 0; c = "yellow"; for (var v in s["comments"]) { var then_test = parseInt(s["comments"][v]["time"]); - if (then_test > then && then_test <= now) + if (then_test > then && s["comments"][v]["state"] != "inactive") then = then_test; + if (s["comments"][v]["state"] == "active") { + active++; + } + if (s["comments"][v]["state"] == "persist") + persist++; } if (then > (now - (60*15))) { - c = "red"; - } else if (then > (now - (120*60))) { - c = "orange"; - } else if (then < (now - (60*60*24))) { - c = "white"; + c = red; + } else if (active > 0) { + c = orange; + } else if (persist > 0) { + c = blue; + } else { + c = green; } - /* - * Special case during time travel: We have - * comments, but are not showing them yet. - */ - if (then == 0) - c = "green"; } setSwitchColor(sw, c); } } + function commentInit() { - setLegend(1,"green","0 comments"); - setLegend(2,"white","1d+ old"); - setLegend(3,"red", "0 - 15m old"); - setLegend(4,"orange","15m - 120m old"); - setLegend(5,"yellow" ,"2h - 24h old"); + setLegend(1,"white","0 comments"); + setLegend(2,blue,"Persistent"); + setLegend(3,red, "New"); + setLegend(4,orange,"Active"); + setLegend(5,green ,"Old/inactive only"); } /* * Testing-function to randomize colors of linknets and switches @@ -274,10 +344,10 @@ function randomizeColors() function discoInit() { setNightMode(true); - setLegend(1,"blue","Y"); - setLegend(2,"red", "M"); - setLegend(3,"yellow","C"); - setLegend(4,"green", "A"); + setLegend(1,blue,"Y"); + setLegend(2,red, "M"); + setLegend(3,orange,"C"); + setLegend(4,green, "A"); setLegend(5,"white","!"); } diff --git a/web/nms.gathering.org/nms2/js/nms.js b/web/nms.gathering.org/nms2/js/nms.js index 41b39d6..d4cf4e8 100644 --- a/web/nms.gathering.org/nms2/js/nms.js +++ b/web/nms.gathering.org/nms2/js/nms.js @@ -1,11 +1,14 @@ var nms = { updater:undefined, // Active updater + update_time:0, // Client side timestamp for last update switches_now:undefined, // Most recent data switches_then:undefined, // 2 minutes old speed:0, // Current aggregated speed ping_data:undefined, // JSON data for ping history. drawn:false, // Set to 'true' when switches are drawn switch_showing:"", // Which switch we are displaying (if any). + repop_switch:false, // True if we need to repopulate the switch info when port state updates (e.g.: added comments); + repop_time:false, // Timestamp in case we get a cached result nightMode:false, /* * Switch-specific variables. These are currently separate from @@ -13,6 +16,8 @@ var nms = { * new data. */ nightBlur:{}, // Have we blurred this switch or not? + shadowBlur:10, + shadowColor:"#EEEEEE", switch_color:{}, // Color for switch linknet_color:{}, // color for linknet textDrawn:{}, // Have we drawn text for this switch? @@ -44,14 +49,41 @@ var nms = { timers: { replay:false, ports:false, - ping:false, - map:false, - speed:false + ping:false }, - deleteComment:0 + menuShowing:true, + /* + * This is a list of nms[x] variables that we store in our + * settings-cookie when altered and restore on load. + */ + settingsList:[ + 'shadowBlur', + 'shadowColor', + 'nightMode', + 'menuShowing', + 'layerVisibility' + ], + layerVisibility:{}, + keyBindings:{ + '?':toggleMenu, + 'n':toggleNightMode, + '1':setMapModeFromN, + '2':setMapModeFromN, + '3':setMapModeFromN, + '4':setMapModeFromN, + '5':setMapModeFromN, + '6':setMapModeFromN, + '7':setMapModeFromN, + 'h':moveTimeFromKey, + 'j':moveTimeFromKey, + 'k':moveTimeFromKey, + 'l':moveTimeFromKey, + 'p':moveTimeFromKey, + 'r':moveTimeFromKey, + 'not-default':keyDebug + } }; - /* * Returns a handler object. * @@ -85,6 +117,7 @@ function nmsTimer(handler, interval, name, description) { }; } + /* * Drawing primitives. * @@ -147,7 +180,7 @@ var margin = { var tgStart = stringToEpoch('2015-04-01T09:00:00'); var tgEnd = stringToEpoch('2015-04-05T12:00:00'); var replayTime = 0; -var replayIncrement = 30 * 60; +var replayIncrement = 60 * 60; /* * Convenience-function to populate the 'dr' structure. @@ -173,6 +206,12 @@ function initDrawing() { dr['top'] = {}; dr['top']['c'] = document.getElementById("topCanvas"); dr['top']['ctx'] = dr['top']['c'].getContext('2d'); + dr['input'] = {}; + dr['input']['c'] = document.getElementById("inputCanvas"); + dr['input']['ctx'] = dr['top']['c'].getContext('2d'); + dr['hidden'] = {}; + dr['hidden']['c'] = document.getElementById("hiddenCanvas"); + dr['hidden']['ctx'] = dr['hidden']['c'].getContext('2d'); } /* @@ -196,6 +235,7 @@ function byteCount(bytes) { function toggleNightMode() { setNightMode(!nms.nightMode); + saveSettings(); } /* @@ -226,7 +266,9 @@ function checkNow(now) */ function stringToEpoch(t) { - var ret = new Date(Date.parse(t)); + var foo = t.toString(); + foo = foo.replace('T',' '); + var ret = new Date(Date.parse(foo)); return parseInt(parseInt(ret.valueOf()) / 1000); } @@ -266,12 +308,15 @@ function epochToString(t) */ function timeReplay() { + replayTime = stringToEpoch(nms.now); if (replayTime >= tgEnd) { nms.timers.replay.stop(); return; } replayTime = parseInt(replayTime) + parseInt(replayIncrement); nms.now = epochToString(replayTime); + updatePorts(); + updatePing(); } /* @@ -289,9 +334,11 @@ function timeReplay() function startReplay() { nms.timers.replay.stop(); resetColors(); - replayTime = tgStart; + nms.now = epochToString(tgStart); timeReplay(); nms.timers.replay.start();; + nms.timers.ping.stop();; + nms.timers.ports.stop();; } /* @@ -307,13 +354,36 @@ function changeNow() { newnow = false; nms.now = newnow; + if (newnow) { + nms.timers.ping.stop();; + nms.timers.ports.stop();; + } updatePorts(); + updatePing(); var boxHide = document.getElementById("nowPickerBox"); if (boxHide) { boxHide.style.display = "none"; } } +function stepTime(n) +{ + var now; + nms.timers.replay.stop(); + nms.timers.ping.stop();; + nms.timers.ports.stop();; + if (nms.now && nms.now != 0) + now = parseInt(stringToEpoch(nms.now)); + else if (nms.switches_now) + now = parseInt(stringToEpoch(/^[^.]*/.exec(nms.switches_now.time))); + else + return; + newtime = parseInt(now) + parseInt(n); + nms.now = epochToString(parseInt(newtime)); + updatePorts(); + updatePing(); +} + /* * Hide switch info-box */ @@ -335,7 +405,7 @@ function hideSwitch() /* * Display info on switch "x" in the info-box */ -function switchInfo(x) +function showSwitch(x) { var sw = nms.switches_now["switches"][x]; var swtop = document.getElementById("info-switch-parent"); @@ -345,13 +415,8 @@ function switchInfo(x) var td1; var td2; - if (nms.switch_showing == x) { - hideSwitch(); - return; - } else { - hideSwitch(); - nms.switch_showing = x; - } + hideSwitch(); + nms.switch_showing = x; document.getElementById("aboutBox").style.display = "none"; var switchele = document.createElement("table"); switchele.id = "info-switch-table"; @@ -490,16 +555,19 @@ function switchInfo(x) if (comment["state"] == "active") col = "danger"; else if (comment["state"] == "inactive") - col = "active"; + col = false; else col = "info"; tr.className = col; + tr.id = "commentRow" + comment["id"]; td1 = tr.insertCell(0); td2 = tr.insertCell(1); - var txt = '<div class="btn-group" role="group" aria-label="..."><button type="button" class="btn btn-xs" data-trigger="focus" data-toggle="popover" title="Info" data-content="Comment added ' + epochToString(comment["time"]) + " by user " + comment["username"] + ' and listed as ' + comment["state"] + '">?</button>'; - txt += '<button type="button" class="btn btn-xs" data-trigger="focus" data-toggle="tooltip" title="Mark as deleted" onclick="commentDelete(' + comment["id"] + ');">X</button>'; - txt += '<button type="button" class="btn btn-xs" data-trigger="focus" data-toggle="tooltip" title="Mark as inactive/fixed" onclick="commentInactive(' + comment["id"] + ');">V</button>'; - txt += '<button type="button" class="btn btn-xs" data-trigger="focus" data-toggle="tooltip" title="Mark as persistent" onclick="commentPersist(' + comment["id"] + ');">!</button></div>'; + td1.style.whiteSpace = "nowrap"; + td1.style.width = "8em"; + var txt = '<div class="btn-group" role="group" aria-label="..."><button type="button" class="btn btn-xs btn-default" data-trigger="focus" data-toggle="popover" title="Info" data-content="Comment added ' + epochToString(comment["time"]) + " by user " + comment["username"] + ' and listed as ' + comment["state"] + '"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span></button>'; + txt += '<button type="button" class="btn btn-xs btn-danger" data-trigger="focus" data-toggle="tooltip" title="Mark as deleted" onclick="commentDelete(' + comment["id"] + ');"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></button>'; + txt += '<button type="button" class="btn btn-xs btn-success" data-trigger="focus" data-toggle="tooltip" title="Mark as inactive/fixed" onclick="commentInactive(' + comment["id"] + ');"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button>'; + txt += '<button type="button" class="btn btn-xs btn-info" data-trigger="focus" data-toggle="tooltip" title="Mark as persistent" onclick="commentPersist(' + comment["id"] + ');"><span class="glyphicon glyphicon-star" aria-hidden="true"></span></button></div>'; td1.innerHTML = txt; td2.textContent = comment['comment']; } @@ -514,7 +582,7 @@ function switchInfo(x) commentbox.id = "commentbox"; commentbox.className = "panel-body"; commentbox.style.width = "100%"; - commentbox.innerHTML = '<div class="form form-inline"><div class="form-group"><input type="text" class="form-control" placeholder="Comment" id="' + x + '-comment"></div><button class="btn btn-default" onclick="addComment(\'' + x + '\',document.getElementById(\'' + x + '-comment\').value); document.getElementById(\'' + x + '-comment\').value = \'added. Wait for it....\';">Add comment</button></div>'; + commentbox.innerHTML = '<div class="input-group"><input type="text" class="form-control" placeholder="Comment" id="' + x + '-comment"><span class=\"input-group-btn\"><button class="btn btn-default" onclick="addComment(\'' + x + '\',document.getElementById(\'' + x + '-comment\').value); document.getElementById(\'' + x + '-comment\').value = \'\'; document.getElementById(\'' + x + '-comment\').placeholder = \'Comment added. Wait for next refresh.\';">Add comment</button></span></div>'; swtop.appendChild(commentbox); swtop.style.display = 'block'; } @@ -530,16 +598,46 @@ function setLegend(x,color,name) { var el = document.getElementById("legend-" + x); el.style.background = color; - el.innerHTML = name; + el.title = name; + el.textContent = name; } +function updateAjaxInfo() +{ + var out = document.getElementById('outstandingAJAX'); + var of = document.getElementById('overflowAJAX'); + out.textContent = nms.outstandingAjaxRequests; + of.textContent = nms.ajaxOverflow; +} /* * Run periodically to trigger map updates when a handler is active */ function updateMap() { + /* + * XXX: This a bit hacky: There are a bunch of links that use + * href="#foo" but probably shouldn't. This breaks refresh since we + * base this on the location hash. To circumvent that issue + * somewhat, we just update the location hash if it's not + * "correct". + */ + if (nms.updater) { + if (document.location.hash != ('#' + nms.updater.tag)) { + document.location.hash = nms.updater.tag; + } + } + if (!newerSwitches()) + return; + if (!nms.drawn) + setScale(); + if (!nms.drawn) + return; + if (!nms.ping_data) + return; + nms.update_time = Date.now(); + if (nms.updater != undefined && nms.switches_now && nms.switches_then) { - nms.updater(); + nms.updater.updater(); } drawNow(); } @@ -552,12 +650,13 @@ function setUpdater(fo) nms.updater = undefined; resetColors(); fo.init(); - nms.updater = fo.updater; + nms.updater = fo; var foo = document.getElementById("updater_name"); foo.innerHTML = fo.name + " "; + document.location.hash = fo.tag; initialUpdate(); if (nms.ping_data && nms.switches_then && nms.switches_now) { - nms.updater(); + nms.updater.updater(); } } @@ -568,17 +667,43 @@ function setUpdater(fo) */ function initialUpdate() { + return; if (nms.ping_data && nms.switches_then && nms.switches_now && nms.updater != undefined && nms.did_update == false ) { resizeEvent(); if (!nms.drawn) { drawSwitches(); drawLinknets(); } - nms.updater(); + nms.updater.updater(); nms.did_update = true; } } +function resetBlur() +{ + nms.nightBlur = {}; + dr.blur.ctx.clearRect(0,0,canvas.width,canvas.height); + drawSwitches(); +} + +function applyBlur() +{ + var blur = document.getElementById("shadowBlur"); + var col = document.getElementById("shadowColor"); + nms.shadowBlur = blur.value; + nms.shadowColor = col.value; + resetBlur(); + saveSettings(); +} + +function showBlurBox() +{ + var blur = document.getElementById("shadowBlur"); + var col = document.getElementById("shadowColor"); + blur.value = nms.shadowBlur; + col.value = nms.shadowColor; + document.getElementById("blurManic").style.display = ''; +} /* * Update nms.ping_data */ @@ -587,6 +712,7 @@ function updatePing() var now = nms.now ? ("?now=" + nms.now) : ""; if (nms.outstandingAjaxRequests > 5) { nms.ajaxOverflow++; + updateAjaxInfo(); return; } nms.outstandingAjaxRequests++; @@ -597,50 +723,73 @@ function updatePing() success: function (data, textStatus, jqXHR) { nms.ping_data = JSON.parse(data); initialUpdate(); + updateMap(); }, complete: function(jqXHR, textStatus) { nms.outstandingAjaxRequests--; + updateAjaxInfo(); } }); } -function commentInactive(id) { +function commentInactive(id) +{ commentChange(id,"inactive"); } -function commentPersist(id) { +function commentPersist(id) +{ commentChange(id,"persist"); } -function commentDelete(id) { - if (id != nms.deleteComment) { - nms.deleteComment = id; - alert("Click the button again to delete it"); - return; + +function commentDelete(id) +{ + var r = confirm("Really delete comment? (Delted comments are still stored in the database, but never displayed)"); + if (r == true) { + commentChange(id,"delete"); } - commentChange(id,"delete"); } -function commentChange(id,state) { + +/* + * FIXME: Neither of these two handle failures in any way, shape or form. + * Nor do they really give user-feedback. They work, but only by magic. + */ +function commentChange(id,state) +{ var myData = { comment:id, state:state }; + var foo = document.getElementById("commentRow" + id); + if (foo) { + foo.className = ''; + foo.style.backgroundColor = "silver"; + } $.ajax({ type: "POST", url: "/comment-change.pl", dataType: "text", - data:myData + data:myData, + success: function (data, textStatus, jqXHR) { + nms.repop_switch = true; + } }); } -function addComment(sw,comment) { + +function addComment(sw,comment) +{ var myData = { switch:sw, - comment:comment}; - console.log(myData); + comment:comment + }; $.ajax({ type: "POST", url: "/switch-comment.pl", dataType: "text", - data:myData + data:myData, + success: function (data, textStatus, jqXHR) { + nms.repop_switch = true; + } }); } /* @@ -651,6 +800,7 @@ function updatePorts() var now = ""; if (nms.outstandingAjaxRequests > 5) { nms.ajaxOverflow++; + updateAjaxInfo(); return; } nms.outstandingAjaxRequests++; @@ -665,15 +815,26 @@ function updatePorts() nms.switches_now = switchdata; parseIntPlacements(); initialUpdate(); + updateSpeed(); + updateMap(); + if (nms.repop_time == false && nms.repop_switch) + nms.repop_time = nms.switches_now.time; + else if (nms.repop_switch && nms.switch_showing && nms.repop_time != nms.switches_now.time) { + showSwitch(nms.switch_showing,true); + nms.repop_switch = false; + nms.repop_time = false; + } }, complete: function(jqXHR, textStatus) { nms.outstandingAjaxRequests--; + updateAjaxInfo(); } }); now=""; if (nms.now != false) now = "&now=" + nms.now; nms.outstandingAjaxRequests++; + updateAjaxInfo(); $.ajax({ type: "GET", url: "/port-state.pl?time=5m" + now, @@ -682,14 +843,32 @@ function updatePorts() var switchdata = JSON.parse(data); nms.switches_then = switchdata; initialUpdate(); + updateSpeed(); + updateMap(); }, complete: function(jqXHR, textStatus) { nms.outstandingAjaxRequests--; + updateAjaxInfo(); } }) } /* + * Returns true if we have now and then-data for switches and that the + * "now" is actually newer. Useful for basic sanity and avoiding negative + * values when rewinding time. + */ +function newerSwitches() +{ + if (!nms.switches_now || !nms.switches_then) + return false; + var now_timestamp = stringToEpoch(nms.switches_now.time); + var then_timestamp = stringToEpoch(nms.switches_then.time); + if (now_timestamp == 0 || then_timestamp == 0 || then_timestamp >= now_timestamp) + return false; + return true; +} +/* * Use nms.switches_now and nms.switches_then to update 'nms.speed'. * * nms.speed is a total of ifHCInOctets across all client-interfaces @@ -703,6 +882,7 @@ function updatePorts() * FIXME: Err, yeah, add this to the tail-end of updatePorts instead :D * */ + function updateSpeed() { var speed_in = parseInt(0); @@ -710,6 +890,8 @@ function updateSpeed() var counter=0; var sw; var speedele = document.getElementById("speed"); + if (!newerSwitches()) + return; for (sw in nms.switches_now["switches"]) { for (port in nms.switches_now["switches"][sw]["ports"]) { if (!nms.switches_now["switches"][sw]["ports"][port]) { @@ -763,8 +945,8 @@ function updateSpeed() */ function drawLinknet(i) { - var c1 = nms.linknet_color[i] && nms.linknet_color[i].c1 ? nms.linknet_color[i].c1 : "blue"; - var c2 = nms.linknet_color[i] && nms.linknet_color[i].c2 ? nms.linknet_color[i].c2 : "blue"; + var c1 = nms.linknet_color[i] && nms.linknet_color[i].c1 ? nms.linknet_color[i].c1 : blue; + var c2 = nms.linknet_color[i] && nms.linknet_color[i].c2 ? nms.linknet_color[i].c2 : blue; if (nms.switches_now.switches[nms.switches_now.linknets[i].sysname1] && nms.switches_now.switches[nms.switches_now.linknets[i].sysname2]) { connectSwitches(nms.switches_now.linknets[i].sysname1,nms.switches_now.linknets[i].sysname2, c1, c2); } @@ -810,16 +992,18 @@ function drawSwitch(sw) var box = nms.switches_now['switches'][sw]['placement']; var color = nms.switch_color[sw]; if (color == undefined) { - color = "blue"; + color = blue; } dr.switch.ctx.fillStyle = color; + /* + * XXX: This is a left-over from before NMS did separate + * canvases, and might be done better elsewhere. + */ if (nms.nightMode && nms.nightBlur[sw] != true) { - dr.switch.ctx.shadowBlur = 10; - dr.switch.ctx.shadowColor = "#00EE00"; + dr.blur.ctx.shadowBlur = nms.shadowBlur; + dr.blur.ctx.shadowColor = nms.shadowColor; + drawBoxBlur(box['x'],box['y'],box['width'],box['height']); nms.nightBlur[sw] = true; - } else { - dr.switch.ctx.shadowBlur = 0; - dr.switch.ctx.shadowColor = "#000000"; } drawBox(box['x'],box['y'],box['width'],box['height']); dr.switch.ctx.shadowBlur = 0; @@ -871,18 +1055,20 @@ function drawSwitches() */ function drawNow() { + if (!nms.switches_now) + return; // XXX: Get rid of microseconds that we get from the backend. var now = /^[^.]*/.exec(nms.switches_now.time); dr.top.ctx.font = Math.round(2 * nms.fontSize * canvas.scale) + "px " + nms.fontFace; dr.top.ctx.clearRect(0,0,Math.floor(800 * canvas.scale),Math.floor(100 * canvas.scale)); dr.top.ctx.fillStyle = "white"; dr.top.ctx.strokeStyle = "black"; - dr.top.ctx.lineWidth = Math.floor(4 * canvas.scale); + dr.top.ctx.lineWidth = Math.floor(2 * canvas.scale); if (dr.top.ctx.lineWidth == 0) { - dr.top.ctx.lineWidth = Math.round(4 * canvas.scale); + dr.top.ctx.lineWidth = Math.round(2 * canvas.scale); } - dr.top.ctx.strokeText(now, 0 + margin.text, 30 * canvas.scale); - dr.top.ctx.fillText(now, 0 + margin.text, 30 * canvas.scale); + dr.top.ctx.strokeText(now, 0 + margin.text, 25 * canvas.scale); + dr.top.ctx.fillText(now, 0 + margin.text, 25 * canvas.scale); } /* * Draw foreground/scene. @@ -907,7 +1093,6 @@ function drawScene() */ function setScale() { - canvas.height = orig.height * canvas.scale ; canvas.width = orig.width * canvas.scale ; for (var a in dr) { @@ -916,8 +1101,11 @@ function setScale() } nms.nightBlur = {}; nms.textDrawn = {}; + if (nms.gradients) + drawGradient(nms.gradients); drawBG(); drawScene(); + drawNow(); document.getElementById("scaler").value = canvas.scale; document.getElementById("scaler-text").innerHTML = (parseFloat(canvas.scale)).toPrecision(3); @@ -978,7 +1166,10 @@ function scaleChange() */ function switchClick(sw) { - switchInfo(sw); + if (nms.switch_showing == sw) + hideSwitch(); + else + showSwitch(sw); } /* @@ -993,11 +1184,11 @@ function resetColors() return; if (nms.switches_now.linknets) { for (var i in nms.switches_now.linknets) { - setLinknetColors(i, "blue","blue"); + setLinknetColors(i, blue,blue); } } for (var sw in nms.switches_now.switches) { - setSwitchColor(sw, "blue"); + setSwitchColor(sw, blue); } } @@ -1058,6 +1249,14 @@ function setNightMode(toggle) { nms.nightMode = toggle; var body = document.getElementById("body"); body.style.background = toggle ? "black" : "white"; + var nav = document.getElementsByTagName("nav")[0]; + if (toggle) { + dr.blur.c.style.display = ''; + nav.classList.add('navbar-inverse'); + } else { + dr.blur.c.style.display = 'none'; + nav.classList.remove('navbar-inverse'); + } setScale(); } /* @@ -1079,6 +1278,17 @@ function drawBox(x,y,boxw,boxh) } /* + * Draw the blur for a box. + */ +function drawBoxBlur(x,y,boxw,boxh) +{ + var myX = Math.floor(x * canvas.scale); + var myY = Math.floor(y * canvas.scale); + var myX2 = Math.floor((boxw) * canvas.scale); + var myY2 = Math.floor((boxh) * canvas.scale); + dr.blur.ctx.fillRect(myX,myY, myX2, myY2); +} +/* * Draw text on a box - sideways! * * XXX: This is pretty nasty and should also probably take a box as input. @@ -1149,9 +1359,9 @@ function connectSwitches(insw1, insw2,color1, color2) { var sw1 = nms.switches_now.switches[insw1].placement; var sw2 = nms.switches_now.switches[insw2].placement; if (color1 == undefined) - color1 = "blue"; + color1 = blue; if (color2 == undefined) - color2 = "blue"; + color2 = blue; var x0 = Math.floor((sw1.x + sw1.width/2) * canvas.scale); var y0 = Math.floor((sw1.y + sw1.height/2) * canvas.scale); var x1 = Math.floor((sw2.x + sw2.width/2) * canvas.scale); @@ -1178,8 +1388,6 @@ function connectSwitches(insw1, insw2,color1, color2) { */ function initNMS() { initDrawing(); - updatePorts(); - updatePing(); window.addEventListener('resize',resizeEvent,true); document.addEventListener('load',resizeEvent,true); @@ -1189,36 +1397,23 @@ function initNMS() { nms.timers.ping = new nmsTimer(updatePing, 1000, "Ping updater", "AJAX request to update ping data"); nms.timers.ping.start(); - nms.timers.map = new nmsTimer(updateMap, 1000, "Map handler", "Updates the map using the chosen map handler (ping, uplink, traffic, etc)"); - nms.timers.map.start(); - - nms.timers.speed = new nmsTimer(updateSpeed, 1000, "Speed updater", "Recompute total speed (no backend requests)"); - nms.timers.speed.start(); - - nms.timers.replay = new nmsTimer(timeReplay, 1000, "Time machine", "Handler used to change time"); + nms.timers.replay = new nmsTimer(timeReplay, 500, "Time machine", "Handler used to change time"); detectHandler(); + updatePorts(); + updatePing(); + setupKeyhandler(); + restoreSettings(); } function detectHandler() { var url = document.URL; - if (/#ping/.exec(url)) { - setUpdater(handler_ping); - }else if (/#uplink/.exec(url)) { - setUpdater(handler_uplinks); - } else if (/#temp/.exec(url)) { - setUpdater(handler_temp); - } else if (/#traffic/.exec(url)) { - setUpdater(handler_traffic); - } else if (/#comment/.exec(url)) { - setUpdater(handler_comment); - } else if (/#disco/.exec(url)) { - setUpdater(handler_disco); - } else { - setUpdater(handler_ping); - } - if (/nightMode/.exec(url)) { - toggleNightMode(); + for (var i in handlers) { + if (('#' + handlers[i].tag) == document.location.hash) { + setUpdater(handlers[i]); + return; + } } + setUpdater(handler_ping); } /* @@ -1227,7 +1422,7 @@ function detectHandler() { * Could probably be cleaned up. */ function showTimerDebug() { - var tableTop = document.getElementById('timerTableTop'); + var tableTop = document.getElementById('debugTimers'); var table = document.getElementById('timerTable'); var tr, td1, td2; if (table) @@ -1238,38 +1433,29 @@ function showTimerDebug() { table.className = "table"; table.classList.add("table"); table.classList.add("table-default"); - table.border = "1"; - tr = document.createElement("tr"); - td = document.createElement("th"); + var header = table.createTHead(); + tr = header.insertRow(0); + td = tr.insertCell(0); td.innerHTML = "Handler"; - tr.appendChild(td); - td = document.createElement("th"); + td = tr.insertCell(1); td.innerHTML = "Interval (ms)"; - tr.appendChild(td); - td = document.createElement("th"); + td = tr.insertCell(2); td.innerHTML = "Name"; - tr.appendChild(td); - td = document.createElement("th"); + td = tr.insertCell(3); td.innerHTML = "Description"; - tr.appendChild(td); - table.appendChild(tr); for (var v in nms.timers) { - console.log(v); - tr = document.createElement("tr"); - td = document.createElement("td"); - td.innerHTML = nms.timers[v].handle; - tr.appendChild(td); - td = document.createElement("td"); - td.innerHTML = "<input type=\"text\" id='handlerValue" + v + "' value='" + nms.timers[v].interval + "'>"; - td.innerHTML += "<button type=\"button\" class=\"btn btn-default\" onclick=\"nms.timers['" + v + "'].setInterval(document.getElementById('handlerValue" + v + "').value);\">Apply</button>"; - tr.appendChild(td); - td = document.createElement("td"); - td.innerHTML = nms.timers[v].name; - tr.appendChild(td); - td = document.createElement("td"); - td.innerHTML = nms.timers[v].description; - tr.appendChild(td); - table.appendChild(tr); + tr = table.insertRow(-1); + td = tr.insertCell(0); + td.textContent = nms.timers[v].handle; + td = tr.insertCell(1); + td.style.width = "15em"; + var tmp = "<div class=\"input-group\"><input type=\"text\" id='handlerValue" + v + "' value='" + nms.timers[v].interval + "' class=\"form-control\"></input>"; + tmp += "<span class=\"input-group-btn\"><button type=\"button\" class=\"btn btn-default\" onclick=\"nms.timers['" + v + "'].setInterval(document.getElementById('handlerValue" + v + "').value);\">Apply</button></span></div>"; + td.innerHTML = tmp; + td = tr.insertCell(2); + td.textContent = nms.timers[v].name; + td = tr.insertCell(3); + td.textContent = nms.timers[v].description; } tableTop.appendChild(table); document.getElementById('debugTimers').style.display = 'block'; @@ -1278,9 +1464,175 @@ function showTimerDebug() { function hideLayer(layer) { var l = document.getElementById(layer); l.style.display = "none"; + if (layer != "layerVisibility") + nms.layerVisibility[layer] = false; + saveSettings(); } function showLayer(layer) { var l = document.getElementById(layer); l.style.display = ""; + if (layer != "layerVisibility") + nms.layerVisibility[layer] = true; + saveSettings(); +} + +function toggleLayer(layer) { + var l = document.getElementById(layer); + if (l.style.display == 'none') + l.style.display = ''; + else + l.style.display = 'none'; +} + +function applyLayerVis() +{ + for (var l in nms.layerVisibility) { + var layer = document.getElementById(l); + if (layer) + layer.style.display = nms.layerVisibility[l] ? '' : 'none'; + } +} + +function setMenu() +{ + var nav = document.getElementsByTagName("nav")[0]; + nav.style.display = nms.menuShowing ? '' : 'none'; + resizeEvent(); + +} +function toggleMenu() +{ + nms.menuShowing = ! nms.menuShowing; + setMenu(); + saveSettings(); +} + +function setMapModeFromN(e,key) +{ + switch(key) { + case '1': + setUpdater(handler_ping); + break; + case '2': + setUpdater(handler_uplinks); + break; + case '3': + setUpdater(handler_temp); + break; + case '4': + setUpdater(handler_traffic); + break; + case '5': + setUpdater(handler_comment); + break; + case '6': + setUpdater(handler_traffic_tot); + break; + case '7': + setUpdater(handler_disco); + break; + } + return true; +} + +function moveTimeFromKey(e,key) +{ + switch(key) { + case 'h': + stepTime(-3600); + break; + case 'j': + stepTime(-300); + break; + case 'k': + stepTime(300); + break; + case 'l': + stepTime(3600); + break; + case 'p': + if(nms.timers.replay.handle) + nms.timers.replay.stop(); + else { + timeReplay(); + nms.timers.replay.start(); + } + break; + case 'r': + nms.timers.replay.stop(); + nms.now = false; + nms.timers.ping.start();; + nms.timers.ports.start();; + updatePorts(); + updatePing(); + break; + } + return true; +} + +var debugEvent; +function keyDebug(e) +{ + console.log(e); + debugEvent = e; +} + +function keyPressed(e) +{ + if (e.target.nodeName == "INPUT") { + return false; + } + var key = String.fromCharCode(e.keyCode); + if (nms.keyBindings[key]) + return nms.keyBindings[key](e,key); + if (nms.keyBindings['default']) + return nms.keyBindings['default'](e,key); + return false; +} + +function setupKeyhandler() +{ + var b = document.getElementsByTagName("body")[0]; + b.onkeypress = function(e){keyPressed(e);}; +} + + +function getCookie(cname) { + var name = cname + "="; + var ca = document.cookie.split(';'); + for(var i=0; i<ca.length; i++) { + var c = ca[i]; + while (c.charAt(0)==' ') + c = c.substring(1); + if (c.indexOf(name) == 0) + return c.substring(name.length,c.length); + } + return ""; +} + +function saveSettings() +{ + var foo={}; + for (var v in nms.settingsList) { + foo[nms.settingsList[v]] = nms[nms.settingsList[v]]; + } + document.cookie = 'nms='+btoa(JSON.stringify(foo)); +} + +function restoreSettings() +{ + var retrieve = JSON.parse(atob(getCookie("nms"))); + for (var v in retrieve) { + nms[v] = retrieve[v]; + } + setScale(); + setMenu(); + setNightMode(nms.nightMode); + applyLayerVis(); +} + +function forgetSettings() +{ + document.cookie = 'nms=' + btoa('{}'); } diff --git a/web/nms.gathering.org/port-state.pl b/web/nms.gathering.org/port-state.pl index b21bd8d..9df31c0 100755 --- a/web/nms.gathering.org/port-state.pl +++ b/web/nms.gathering.org/port-state.pl @@ -69,7 +69,12 @@ while (my $ref = $q4->fetchrow_hashref()) { # push @{$json{'linknets'}}, $ref; } -my $q5 = $dbh->prepare ('select ' . $now . ' as time;'); +my $q5; +if (defined($cin)) { + $q5 = $dbh->prepare ('select (' . $now . ' - \'' . $cin . '\'::interval) as time;'); +} else { + $q5 = $dbh->prepare ('select ' . $now . ' as time;'); +} $q5->execute(); $json{'time'} = $q5->fetchrow_hashref()->{'time'}; diff --git a/web/nms.gathering.org/uplinkkart-text.pl b/web/nms.gathering.org/uplinkkart-text.pl index de91e92..c4b31a9 100755 --- a/web/nms.gathering.org/uplinkkart-text.pl +++ b/web/nms.gathering.org/uplinkkart-text.pl @@ -17,7 +17,7 @@ print <<"EOF"; <map name="switches"> EOF -my $q = $dbh->prepare("SELECT * FROM switches NATURAL JOIN placements WHERE switchtype = 'dlink3100'"); +my $q = $dbh->prepare("SELECT * FROM switches NATURAL JOIN placements WHERE switchtype = 'ex2200'"); $q->execute(); while (my $ref = $q->fetchrow_hashref()) { $ref->{'placement'} =~ /\((\d+),(\d+)\),\((\d+),(\d+)\)/; |