diff options
author | Kristian Lyngstol <kristian@bohemians.org> | 2015-04-25 19:10:33 +0200 |
---|---|---|
committer | Kristian Lyngstol <kristian@bohemians.org> | 2015-04-25 19:10:33 +0200 |
commit | 867d787c4ab152719dfe13b1f13ab3e303a4780e (patch) | |
tree | aec1a96709f86faa3cb2cedf53008ec0d81a6554 | |
parent | c7ebb7269d9ea8ead03b717153bfa54e3c8226c9 (diff) |
NMS: Monster-commit after local .git corruption
This constitutes ... I don't know.... 20 commits or something like that.
Unfortunately my local .git directory got corrupted while the check out was
OK (after the VM froze). Instead of trying to recreate the local history,
I'm just hurrying the hell up and getting this work OFF my local laptop and
VM so I don't lose anything for good.
-rw-r--r-- | sql/dump.sql | 1075 | ||||
-rw-r--r-- | web/etc/varnish/default.vcl | 2 | ||||
-rw-r--r-- | web/nms.gathering.org/nms2/index.html | 637 | ||||
-rw-r--r-- | web/nms.gathering.org/nms2/js/nms-color-util.js | 36 | ||||
-rw-r--r-- | web/nms.gathering.org/nms2/js/nms-map-handlers.js | 127 | ||||
-rw-r--r-- | web/nms.gathering.org/nms2/js/nms.js | 499 | ||||
-rwxr-xr-x | web/nms.gathering.org/port-state.pl | 7 | ||||
-rwxr-xr-x | web/nms.gathering.org/uplinkkart-text.pl | 2 |
8 files changed, 2017 insertions, 368 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/index.html b/web/nms.gathering.org/nms2/index.html index 42d7a35..141ef5f 100644 --- a/web/nms.gathering.org/nms2/index.html +++ b/web/nms.gathering.org/nms2/index.html @@ -21,91 +21,108 @@ <!--[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="#disco" onclick="setUpdater(handler_disco)">DISCO</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 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 +147,351 @@ <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="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 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> + </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 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" 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"> - - <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> + <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-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>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>Add DHCP map</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>Comments: Fix feedback, possibly update the switch-box + too, when comments change.</li> + </ul> + <h3>Todo for backend:</h3> + <ul> + <li>IPv6 support</li> + <li>Provide public API's</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?</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> -<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-full.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 +499,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..8ac4ab8 100644 --- a/web/nms.gathering.org/nms2/js/nms-color-util.js +++ b/web/nms.gathering.org/nms2/js/nms-color-util.js @@ -1,4 +1,20 @@ +/* + * 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) { @@ -12,21 +28,21 @@ function gradient_from_latency(latency_ms, latency_secondary_ms) function rgb_from_latency(latency_ms) { if (latency_ms === null || latency_ms === undefined) { - return '#0000ff'; + return blue; } - var l = latency_ms / 40.0; + var l = latency_ms / 50.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); + l = Math.floor(l * 205.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)'; + return 'rgb(' + l + ', 205, 0)'; } } @@ -42,13 +58,21 @@ function rgb_from_max(x) return 'rgb(' + Math.floor(colorred) + ", 0, " + Math.floor(colorblue) + ')'; } +function rgb_from_max2(x) +{ + x = x/100; + var colorred = 255 * x; + var colorgreen = 250 - colorred; + + return 'rgb(' + Math.floor(colorred) + "," + Math.floor(colorgreen) + ', 0 )'; +} /* * Return a random-ish color (for testing) */ function getRandomColor() { - var i = Math.round(Math.random() * 5); - var colors = [ "white", "red", "pink", "yellow", "orange", "green" ]; + var colors = [ "white", red, teal, orange, green, blue ]; + var i = Math.round(Math.random() * (colors.length-1)); return colors[i]; } 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..f85b06f 100644 --- a/web/nms.gathering.org/nms2/js/nms-map-handlers.js +++ b/web/nms.gathering.org/nms2/js/nms-map-handlers.js @@ -53,6 +53,7 @@ var handler_comment = { init:commentInit, name:"Fresh comment spotter" }; + /* * Update function for uplink map * Run periodically when uplink map is active. @@ -78,15 +79,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 +95,26 @@ 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; + setLegend(1,colorFromSpeed(0),"0 (N/A)"); + setLegend(5,colorFromSpeed(2000 * m) , "2000Mb/s"); + setLegend(4,colorFromSpeed(1200 * m),"1200Mb/s"); + setLegend(3,colorFromSpeed(500 * m),"500Mb/s"); + setLegend(2,colorFromSpeed(10 * m),"10Mb/s"); } function trafficUpdater() @@ -115,37 +129,27 @@ 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 colorFromSpeed(speed) { - 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; + if (speed == 0) + return blue; + speed = speed < 0 ? 0 : speed; + return rgb_from_max2( 100 * (speed / (2 * (1000 * m)))); } @@ -158,7 +162,7 @@ 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); @@ -189,13 +193,13 @@ function tempInit() function pingUpdater() { 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]); @@ -210,53 +214,54 @@ function pingInit() 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,blue ,"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 comments"); + setLegend(3,red, "New comments"); + setLegend(4,orange,"Active comments"); + setLegend(5,green ,"Old/inactive only"); } /* * Testing-function to randomize colors of linknets and switches @@ -274,10 +279,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..cf2690e 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,40 @@ 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, + 'h':moveTimeFromKey, + 'j':moveTimeFromKey, + 'k':moveTimeFromKey, + 'l':moveTimeFromKey, + 'p':moveTimeFromKey, + 'r':moveTimeFromKey, + 'default':keyDebug + } }; - /* * Returns a handler object. * @@ -85,6 +116,7 @@ function nmsTimer(handler, interval, name, description) { }; } + /* * Drawing primitives. * @@ -173,6 +205,9 @@ 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'); } /* @@ -196,6 +231,7 @@ function byteCount(bytes) { function toggleNightMode() { setNightMode(!nms.nightMode); + saveSettings(); } /* @@ -226,7 +262,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,6 +304,7 @@ function epochToString(t) */ function timeReplay() { + replayTime = stringToEpoch(nms.now); if (replayTime >= tgEnd) { nms.timers.replay.stop(); return; @@ -289,7 +328,7 @@ function timeReplay() function startReplay() { nms.timers.replay.stop(); resetColors(); - replayTime = tgStart; + nms.now = epochToString(tgStart); timeReplay(); nms.timers.replay.start();; } @@ -314,6 +353,21 @@ function changeNow() { } } +function stepTime(n) +{ + var now; + nms.timers.replay.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(); +} + /* * Hide switch info-box */ @@ -335,7 +389,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 +399,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 +539,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 +566,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,14 +582,28 @@ 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() { + if (!newerSwitches()) + return; + if (!(nms.update_time < (Date.now() - 100) || nms.update_time == 0)) + return; + nms.update_time = Date.now(); + if (nms.updater != undefined && nms.switches_now && nms.switches_then) { nms.updater(); } @@ -579,6 +645,31 @@ function initialUpdate() } } +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 +678,7 @@ function updatePing() var now = nms.now ? ("?now=" + nms.now) : ""; if (nms.outstandingAjaxRequests > 5) { nms.ajaxOverflow++; + updateAjaxInfo(); return; } nms.outstandingAjaxRequests++; @@ -597,50 +689,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 +766,7 @@ function updatePorts() var now = ""; if (nms.outstandingAjaxRequests > 5) { nms.ajaxOverflow++; + updateAjaxInfo(); return; } nms.outstandingAjaxRequests++; @@ -665,15 +781,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 +809,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 +848,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 +856,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 +911,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 +958,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 +1021,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. @@ -918,6 +1070,7 @@ function setScale() nms.textDrawn = {}; drawBG(); drawScene(); + drawNow(); document.getElementById("scaler").value = canvas.scale; document.getElementById("scaler-text").innerHTML = (parseFloat(canvas.scale)).toPrecision(3); @@ -978,7 +1131,10 @@ function scaleChange() */ function switchClick(sw) { - switchInfo(sw); + if (nms.switch_showing == sw) + hideSwitch(); + else + showSwitch(sw); } /* @@ -993,11 +1149,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 +1214,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 +1243,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 +1324,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); @@ -1189,14 +1364,10 @@ 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"); detectHandler(); + setupKeyhandler(); + restoreSettings(); } function detectHandler() { @@ -1216,9 +1387,6 @@ function detectHandler() { } else { setUpdater(handler_ping); } - if (/nightMode/.exec(url)) { - toggleNightMode(); - } } /* @@ -1227,7 +1395,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 +1406,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 +1437,169 @@ 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_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; + updatePorts(); + 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+)\)/; |