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+)\)/;  | 
