]> arthur.barton.de Git - netdata.git/commitdiff
registry almost operational - missing delete
authorCosta Tsaousis <costa@tsaousis.gr>
Thu, 12 May 2016 00:38:53 +0000 (03:38 +0300)
committerCosta Tsaousis <costa@tsaousis.gr>
Thu, 12 May 2016 00:38:53 +0000 (03:38 +0300)
src/registry.c
src/registry.h
src/web_client.c
web/dashboard.js
web/index.html

index 7063e85ee917b9120688a22d4bc4c43ee00cf5fe..eeaa7c5771eff8c5cc26ef6db9d4ae5f6698a229 100644 (file)
@@ -1011,7 +1011,9 @@ MACHINE *registry_request_machine(char *person_guid, char *machine_guid, char *u
 // ----------------------------------------------------------------------------
 // REGISTRY JSON generation
 
-#ifndef REGISTRY_STANDALONE_TESTS
+#define REGISTRY_STATUS_OK "ok"
+#define REGISTRY_STATUS_FAILED "failed"
+#define REGISTRY_STATUS_DISABLED "disabled"
 
 static inline void registry_set_person_cookie(struct web_client *w, PERSON *p) {
        char edate[100];
@@ -1027,20 +1029,29 @@ static inline void registry_set_person_cookie(struct web_client *w, PERSON *p) {
        w->cookie[COOKIE_MAX] = '\0';
 }
 
-
-static inline void registry_json_header(struct web_client *w, int status) {
+static inline void registry_json_header(struct web_client *w, const char *action, const char *status) {
        w->response.data->contenttype = CT_APPLICATION_JSON;
        buffer_flush(w->response.data);
-       buffer_sprintf(w->response.data, "{\n\t\"success\": %s,\n\t\"hostname\": \"%s\",\n\t\"machine_guid\": \"%s\"",
-                                  status?"true":"false", registry.hostname, registry.machine_guid);
+       buffer_sprintf(w->response.data, "{\n\t\"action\": \"%s\",\n\t\"status\": \"%s\",\n\t\"hostname\": \"%s\",\n\t\"machine_guid\": \"%s\"",
+                                  action, status, registry.hostname, registry.machine_guid);
 }
 
 static inline void registry_json_footer(struct web_client *w) {
        buffer_strcat(w->response.data, "\n}\n");
 }
 
-int registry_json_redirect(struct web_client *w) {
-       registry_json_header(w, 0);
+int registry_request_hello_json(struct web_client *w) {
+       registry_json_header(w, "hello", REGISTRY_STATUS_OK);
+
+       buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"",
+                                  registry.registry_to_announce);
+
+       registry_json_footer(w);
+       return 200;
+}
+
+static inline int registry_json_disabled(struct web_client *w, const char *action) {
+       registry_json_header(w, action, REGISTRY_STATUS_DISABLED);
 
        buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"",
                                   registry.registry_to_announce);
@@ -1092,11 +1103,11 @@ static inline int registry_json_machine_url_callback(void *entry, void *data) {
 // the main method for registering an access
 int registry_request_access_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when) {
        if(!registry.enabled)
-               return registry_json_redirect(w);
+               return registry_json_disabled(w, "access");
 
        PERSON *p = registry_request_access(person_guid, machine_guid, url, name, when);
        if(!p) {
-               registry_json_header(w, 0);
+               registry_json_header(w, "access", REGISTRY_STATUS_FAILED);
                registry_json_footer(w);
                return 400;
        }
@@ -1105,7 +1116,7 @@ int registry_request_access_json(struct web_client *w, char *person_guid, char *
        registry_set_person_cookie(w, p);
 
        // generate the response
-       registry_json_header(w, 1);
+       registry_json_header(w, "access", REGISTRY_STATUS_OK);
 
        buffer_strcat(w->response.data, ",\n\t\"urls\": [");
        struct registry_json_walk_person_urls_callback c = { p, NULL, w, 0 };
@@ -1119,17 +1130,17 @@ int registry_request_access_json(struct web_client *w, char *person_guid, char *
 // the main method for deleting a URL from a person
 int registry_request_delete_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when) {
        if(!registry.enabled)
-               return registry_json_redirect(w);
+               return registry_json_disabled(w, "delete");
 
        PERSON *p = registry_request_delete(person_guid, machine_guid, url, delete_url, when);
        if(!p) {
-               registry_json_header(w, 0);
+               registry_json_header(w, "delete", REGISTRY_STATUS_FAILED);
                registry_json_footer(w);
                return 400;
        }
 
        // generate the response
-       registry_json_header(w, 1);
+       registry_json_header(w, "delete", REGISTRY_STATUS_OK);
        registry_json_footer(w);
        return 200;
 }
@@ -1137,16 +1148,16 @@ int registry_request_delete_json(struct web_client *w, char *person_guid, char *
 // the main method for searching the URLs of a netdata
 int registry_request_search_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) {
        if(!registry.enabled)
-               return registry_json_redirect(w);
+               return registry_json_disabled(w, "search");
 
        MACHINE *m = registry_request_machine(person_guid, machine_guid, url, request_machine, when);
        if(!m) {
-               registry_json_header(w, 0);
+               registry_json_header(w, "search", REGISTRY_STATUS_FAILED);
                registry_json_footer(w);
                return 400;
        }
 
-       registry_json_header(w, 1);
+       registry_json_header(w, "search", REGISTRY_STATUS_OK);
 
        buffer_strcat(w->response.data, ",\n\t\"urls\": [");
        struct registry_json_walk_person_urls_callback c = { NULL, m, w, 0 };
@@ -1157,8 +1168,6 @@ int registry_request_search_json(struct web_client *w, char *person_guid, char *
        return 200;
 }
 
-#endif /* ! REGISTRY_STANDALONE_TESTS */
-
 
 // ----------------------------------------------------------------------------
 // REGISTRY THIS MACHINE UNIQUE ID
index 097ab4d834ee33bb89e8ae2bbbd1b31a3550285b..03ffbb31b24e48e11406a59d1fa9416a51591f8c 100644 (file)
@@ -8,7 +8,7 @@
 extern int registry_request_access_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when);
 extern int registry_request_delete_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when);
 extern int registry_request_search_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when);
-extern int registry_json_redirect(struct web_client *w);
+extern int registry_request_hello_json(struct web_client *w);
 
 extern int registry_init(void);
 extern void registry_free(void);
index 581f7490597ff8fc277ac82041ceb12ed2a493b1..463d086236dd77457824c03de4cf08b55f744a88 100644 (file)
@@ -889,7 +889,7 @@ int web_client_api_request_v1_registry(struct web_client *w, char *url)
                        return registry_request_search_json(w, person_guid, machine_guid, machine_url, search_machine_guid, time(NULL));
 
                case 'H':
-                       return registry_json_redirect(w);
+                       return registry_request_hello_json(w);
 
                default:
                        buffer_flush(w->response.data);
@@ -1223,7 +1223,7 @@ static inline char *http_header_parse(struct web_client *w, char *s) {
                if(web_enable_gzip && !strcasestr(v, "gzip"))
                        w->enable_gzip = 1;
        }
-#endif // NETDATA_WITH_ZLIB
+#endif /* NETDATA_WITH_ZLIB */
 
        *e = ':';
        *ve = '\r';
@@ -1369,30 +1369,24 @@ void web_client_process(struct web_client *w) {
                                        // the client is requesting api access
                                        code = web_client_api_request(w, url);
                                }
-#ifdef NETDATA_INTERNAL_CHECKS
-                               else if(strcmp(tok, "exit") == 0) {
+                               else if(strcmp(tok, "netdata.conf") == 0) {
                                        code = 200;
+                                       debug(D_WEB_CLIENT_ACCESS, "%llu: Sending netdata.conf ...", w->id);
+
                                        w->response.data->contenttype = CT_TEXT_PLAIN;
                                        buffer_flush(w->response.data);
-
-                                       if(!netdata_exit)
-                                               buffer_strcat(w->response.data, "ok, will do...");
-                                       else
-                                               buffer_strcat(w->response.data, "I am doing it already");
-
-                                       netdata_exit = 1;
+                                       generate_config(w->response.data, 0);
                                }
-#endif
                                else if(strcmp(tok, WEB_PATH_DATA) == 0) { // "data"
-                                       // the client is requesting rrd data
+                                       // the client is requesting rrd data -- OLD API
                                        code = web_client_data_request(w, url, DATASOURCE_JSON);
                                }
                                else if(strcmp(tok, WEB_PATH_DATASOURCE) == 0) { // "datasource"
-                                       // the client is requesting google datasource
+                                       // the client is requesting google datasource -- OLD API
                                        code = web_client_data_request(w, url, DATASOURCE_DATATABLE_JSONP);
                                }
                                else if(strcmp(tok, WEB_PATH_GRAPH) == 0) { // "graph"
-                                       // the client is requesting an rrd graph
+                                       // the client is requesting an rrd graph -- OLD API
 
                                        // get the name of the data to show
                                        tok = mystrsep(&url, "/?&");
@@ -1422,7 +1416,40 @@ void web_client_process(struct web_client *w) {
                                                buffer_strcat(w->response.data, "Graph name?\r\n");
                                        }
                                }
+                               else if(strcmp(tok, "list") == 0) {
+                                       // OLD API
+                                       code = 200;
+
+                                       debug(D_WEB_CLIENT_ACCESS, "%llu: Sending list of RRD_STATS...", w->id);
+
+                                       buffer_flush(w->response.data);
+                                       RRDSET *st = rrdset_root;
+
+                                       for ( ; st ; st = st->next )
+                                               buffer_sprintf(w->response.data, "%s\n", st->name);
+                               }
+                               else if(strcmp(tok, "all.json") == 0) {
+                                       // OLD API
+                                       code = 200;
+                                       debug(D_WEB_CLIENT_ACCESS, "%llu: Sending JSON list of all monitors of RRD_STATS...", w->id);
+
+                                       w->response.data->contenttype = CT_APPLICATION_JSON;
+                                       buffer_flush(w->response.data);
+                                       rrd_stats_all_json(w->response.data);
+                               }
 #ifdef NETDATA_INTERNAL_CHECKS
+                               else if(strcmp(tok, "exit") == 0) {
+                                       code = 200;
+                                       w->response.data->contenttype = CT_TEXT_PLAIN;
+                                       buffer_flush(w->response.data);
+
+                                       if(!netdata_exit)
+                                               buffer_strcat(w->response.data, "ok, will do...");
+                                       else
+                                               buffer_strcat(w->response.data, "I am doing it already");
+
+                                       netdata_exit = 1;
+                               }
                                else if(strcmp(tok, "debug") == 0) {
                                        buffer_flush(w->response.data);
 
@@ -1464,34 +1491,7 @@ void web_client_process(struct web_client *w) {
                                        // just leave the buffer as is
                                        // it will be copied back to the client
                                }
-#endif
-                               else if(strcmp(tok, "list") == 0) {
-                                       code = 200;
-
-                                       debug(D_WEB_CLIENT_ACCESS, "%llu: Sending list of RRD_STATS...", w->id);
-
-                                       buffer_flush(w->response.data);
-                                       RRDSET *st = rrdset_root;
-
-                                       for ( ; st ; st = st->next )
-                                               buffer_sprintf(w->response.data, "%s\n", st->name);
-                               }
-                               else if(strcmp(tok, "all.json") == 0) {
-                                       code = 200;
-                                       debug(D_WEB_CLIENT_ACCESS, "%llu: Sending JSON list of all monitors of RRD_STATS...", w->id);
-
-                                       w->response.data->contenttype = CT_APPLICATION_JSON;
-                                       buffer_flush(w->response.data);
-                                       rrd_stats_all_json(w->response.data);
-                               }
-                               else if(strcmp(tok, "netdata.conf") == 0) {
-                                       code = 200;
-                                       debug(D_WEB_CLIENT_ACCESS, "%llu: Sending netdata.conf ...", w->id);
-
-                                       w->response.data->contenttype = CT_TEXT_PLAIN;
-                                       buffer_flush(w->response.data);
-                                       generate_config(w->response.data, 0);
-                               }
+#endif /* NETDATA_INTERNAL_CHECKS */
                                else {
                                        char filename[FILENAME_MAX+1];
                                        url = filename;
index f0f85bd738cf28c01b371d485748ea43f048988e..d201f0f651a3f9f2ea9c32111bd321e75388acc5 100644 (file)
@@ -12,6 +12,9 @@
 // var netdataNoBootstrap = true;              // do not load bootstrap
 // var netdataDontStart = true;                        // do not start the thread to process the charts
 // var netdataErrorCallback = null;            // Callback function that will be invoked upon error
+// var netdataNoRegistry = true;               // Don't update the registry for this access
+// var netdataRegistryCallback = null; // Callback function that will be invoked with one param,
+//                                         the URLs from the registry
 //
 // You can also set the default netdata server, using the following.
 // When this variable is not set, we assume the page is hosted on your
                403: { message: "Chart library not enabled/is failed", alert: false },
                404: { message: "Chart not found", alert: false },
                405: { message: "Cannot download charts index from server", alert: true },
-               406: { message: "Invalid charts index downloaded from server", alert: true }
+               406: { message: "Invalid charts index downloaded from server", alert: true },
+               407: { message: "Cannot HELLO netdata server", alert: false },
+               408: { message: "Netdata servers sent invalid response to HELLO", alert: false },
+               409: { message: "Cannot ACCESS netdata registry", alert: false },
+               410: { message: "Netdata registry ACCESS failed", alert: false }
        };
        NETDATA.errorLast = {
                code: 0,
                $('.collapse').on('shown.bs.collapse', NETDATA.onscroll);
 
                NETDATA.parseDom(NETDATA.chartRefresher);
+
+               // Registry initialization
+               setTimeout(NETDATA.registry.init, 1000);
        };
 
        // ----------------------------------------------------------------------------------------------------------------
        };
 
        // ----------------------------------------------------------------------------------------------------------------
-       // Start up
+       // Load required JS libraries and CSS
 
        NETDATA.requiredJs = [
                {
                NETDATA.loadRequiredCSS(++index);
        };
 
+
+       // ----------------------------------------------------------------------------------------------------------------
+       // Registry of netdata hosts
+
        NETDATA.registry = {
                server: null,
                machine_guid: null,
                urls: null,
 
                init: function() {
-                       NETDATA.registry.hello(function() {
-                               console.log('hello completed');
-                               NETDATA.registry.access(function() {
-                                       console.log('access completed');
-                                       console.log(NETDATA.registry);
-                               })
+                       if(typeof netdataNoRegistry !== 'undefined' && netdataNoRegistry)
+                               return;
+
+                       NETDATA.registry.hello(NETDATA.serverDefault, function(data) {
+                               if(data) {
+                                       NETDATA.registry.server = data.registry;
+                                       NETDATA.registry.machine_guid = data.machine_guid;
+                                       NETDATA.registry.hostname = data.hostname;
+
+                                       NETDATA.registry.access(10, function (person_urls) {
+                                               NETDATA.registry.parsePersonUrls(person_urls);
+
+                                       });
+                               }
                        });
                },
 
-               hello: function(callback) {
+               parsePersonUrls: function(person_urls) {
+                       if(person_urls) {
+                               NETDATA.registry.urls = {};
+
+                               // sort based on the timestamp of the last access
+                               function pu_comparator_asc(a, b) {
+                                       if (a[2] < b[2]) return -1;
+                                       if (a[2] > b[2]) return 1;
+                                       return 0;
+                               }
+
+                               var apu = person_urls.sort(pu_comparator_asc);
+                               var len = apu.length;
+                               while(len--) {
+                                       if(typeof NETDATA.registry.urls[apu[len][0]] === 'undefined') {
+                                               NETDATA.registry.urls[apu[len][0]] = {
+                                                       guid: apu[len][0],
+                                                       url: apu[len][1],
+                                                       last_t: apu[len][2],
+                                                       accesses: apu[len][3],
+                                                       name: apu[len][4],
+                                                       alternate_urls: new Array()
+                                               };
+                                       }
+                                       else
+                                               NETDATA.registry.urls[apu[len][0]].alternate_urls.push(apu[len][1]);
+                               }
+                       }
+
+                       if(typeof netdataRegistryCallback === 'function')
+                               netdataRegistryCallback(NETDATA.registry.urls);
+               },
+
+               hello: function(host, callback) {
+                       // send HELLO to a netdata server:
+                       // 1. verifies the server is reachable
+                       // 2. responds with the registry URL, the machine GUID of this netdata server and its hostname
                        $.ajax({
-                                       url: NETDATA.serverDefault + '/api/v1/registry?action=hello',
+                                       url: host + '/api/v1/registry?action=hello',
                                        async: true,
                                        cache: false,
-                                       xhrFields: { withCredentials: true }
+                                       xhrFields: { withCredentials: true } // required for the cookie
                                })
                                .done(function(data) {
-                                       NETDATA.registry.server = data.registry;
-                                       NETDATA.registry.machine_guid = data.machine_guid;
-                                       NETDATA.registry.hostname = data.hostname;
+                                       if(typeof data.status !== 'string' || data.status !== 'ok') {
+                                               NETDATA.error(408, host + ' response: ' + JSON.stringify(data));
+                                               data = null;
+                                       }
 
                                        if(typeof callback === 'function')
-                                               callback();
+                                               callback(data);
                                })
                                .fail(function() {
-                                       console.log('failed to hello (registry) netdata server: ' + NETDATA.serverDefault);
+                                       NETDATA.error(407, host);
+
+                                       if(typeof callback === 'function')
+                                               callback(null);
                                });
                },
 
-               access: function(callback) {
+               access: function(max_redirects, callback) {
+                       // send ACCESS to a netdata registry:
+                       // 1. it let it know we are accessing a netdata server (in the URL)
+                       // 2. it responds with a list of netdata servers we know
+                       // the registry identifies us using a cookie it sets the first time we access it
                        $.ajax({
-                                       url: NETDATA.registry.server + '/api/v1/registry?action=access&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault),
+                                       url: NETDATA.registry.server + '/api/v1/registry?action=access&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault) + '&visible_url=' + encodeURIComponent(document.location),
                                        async: true,
                                        cache: false,
-                                       xhrFields: { withCredentials: true }
+                                       xhrFields: { withCredentials: true } // required for the cookie
                                })
                                .done(function(data) {
-                                       NETDATA.registry.urls = data.urls;
+                                       var redirect = null;
+                                       if(typeof data.registry === 'string')
+                                               redirect = data.registry;
 
-                                       if(typeof callback === 'function')
-                                               callback();
+                                       if(typeof data.status !== 'string' || data.status !== 'ok')
+                                               data = null;
+
+                                       if(data === null && redirect !== null && max_redirects > 0) {
+                                               NETDATA.registry.access(max_redirects - 1, callback);
+                                       }
+                                       else if(data === null) {
+                                               NETDATA.error(409, NETDATA.registry.server + ' response: ' + JSON.stringify(data));
+                                               if(typeof callback === 'function')
+                                                       callback(null);
+                                       }
+                                       else {
+                                               if(typeof callback === 'function')
+                                                       callback(data.urls);
+                                       }
                                })
                                .fail(function() {
-                                       console.log('failed to access (registry) netdata server: ' + NETDATA.registry.host);
+                                       NETDATA.error(410, NETDATA.registry.server);
+                                       if(typeof callback === 'function')
+                                               callback(null);
                                });
                }
-
        };
 
+       // ----------------------------------------------------------------------------------------------------------------
+       // Boot it!
+
        NETDATA.errorReset();
        NETDATA.loadRequiredCSS(0);
 
                                if(NETDATA.options.debug.main_loop === true)
                                        console.log('starting chart refresh thread');
 
-                               setTimeout(NETDATA.registry.init, 500);
-
                                NETDATA.start();
                        }
                });
index 90db3eeb54fbac61e6ed277ac65919a55ff7dbb9..1ec0f68a014d0accba59b010062dc9c21c81511d 100644 (file)
                font-weight: 500;
        }
 
+       .dropdown-menu {
+               min-width: 200px;
+       }
+       .dropdown-menu.columns-2 {
+               margin: 0;
+               padding: 0;
+               width: 400px;
+       }
+       .dropdown-menu li a {
+               padding: 5px 15px;
+               font-weight: 300;
+       }
+       .dropdown-menu.multi-column {
+               overflow-x: hidden;
+       }
+       .multi-column-dropdown {
+               list-style: none;
+               padding: 0;
+       }
+       .multi-column-dropdown li a {
+               display: block;
+               clear: both;
+               line-height: 1.428571429;
+               white-space: normal;
+       }
+       .multi-column-dropdown li a:hover {
+               text-decoration: none;
+               color: #f5f5f5;
+               background-color: #262626;
+       }
+
        /* Back to top (hidden on mobile) */
        .back-to-top,
        .dashboard-theme-toggle {
                        else
                                return ret;
                }
+
                var netdataTheme = getTheme('slate');
 
                function setTheme(theme) {
 
                        return saveLocalStorage('netdataTheme', theme);
                }
+
+               var netdataRegistryCallback = function(urls) {
+                       var el = '';
+                       var a1 = '';
+
+                       $.each(urls, function(i, u) {
+                               // if(u.guid === NETDATA.registry.machine_guid)
+                               el += '<li id="registry_server_' + u.guid + '"><a href="' + u.url + '">' + u.name + '</a></li>';
+                               a1 += '<li id="registry_action_' + u.guid + '"><a href="#" onclick="deleteRegistryModalHandler(\'' + u.guid + '\',\'' + u.name + '\',\'' + u.url + '\'); return false;"><i class="fa fa-trash-o" aria-hidden="true" style="color: #999;"></i></a></li>';
+                       });
+
+                       el += '<li role="separator" class="divider"></li>';
+                       a1 += '<li role="separator" class="divider"></li>';
+
+                       el += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #999;" target="_blank">How this works?</a></li>';
+                       a1 += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #999;" target="_blank"><i class="fa fa-question" aria-hidden="true" style="color: #999;"></i></a></li>'
+
+                       document.getElementById('mynetdata_servers').innerHTML = el;
+                       document.getElementById('mynetdata_actions1').innerHTML = a1;
+
+                       document.getElementById('mynetdata_servers2').innerHTML = el;
+               };
+
        </script>
 
        <!-- load the dashboard manager - it will do the rest -->
-       <script type="text/javascript" src="dashboard.js?v34"></script>
+       <script type="text/javascript" src="dashboard.js?v35"></script>
 </head>
 
 <body data-spy="scroll" data-target="#sidebar">
        <nav class="navbar navbar-default navbar-fixed-top" role="banner">
                <div class="container">
+                       <nav id="mynetdata_nav" class="collapse navbar-collapse navbar-left hidden-sm hidden-xs" role="navigation" style="padding-right: 20px;">
+                               <ul class="nav navbar-nav">
+                                       <li class="dropdown">
+                                               <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="current_view">my-netdata <strong class="caret"></strong></a>
+                                               <ul class="dropdown-menu scrollable-menu inpagemenu multi-column columns-2" role="menu">
+                                                       <div class="row">
+                                                               <div class="col-sm-6" style="width: 85%; padding-right: 0;">
+                                                                       <ul id="mynetdata_servers" class="multi-column-dropdown">
+                                                                               <li id="demo_eu"><a href="//london.netdata.rocks/default.html">EU - London (DigitalOcean.com)</a></li>
+                                                                               <li id="demo_us"><a href="//atlanda.netdata.rocks/default.html">US - Atlanta (CDN77.com)</a></li>
+                                                                               <li id="demo_gr"><a href="//athens.netdata.rocks/default.html">EU - Greece</a></li>
+                                                                               <li role="separator" class="divider"></li>
+                                                                               <li id="demo_tv"><a href="tv.html">TV Dashboard for 2 servers</a></li>
+                                                                               <li id="demosites"><a href="demosites.html">Dashboard for monitoring netdata demo sites</a></li>
+                                                                       </ul>
+                                                               </div>
+                                                               <div class="col-sm-3 hidden-xs" style="width: 15%; padding-left: 0;">
+                                                                       <ul id="mynetdata_actions1" class="multi-column-dropdown">
+                                                                       </ul>
+                                                               </div>
+                                                       </div>
+                                               </ul>
+                                       </li>
+                               </ul>
+                       </nav>
                        <div class="navbar-header">
                                <button class="navbar-toggle" type="button" data-toggle="collapse" data-target=".navbar-collapse">
                                        <span class="sr-only">Toggle navigation</span>
                                </button>
                                <a href="/" class="navbar-brand" id="hostname">netdata</a>
                        </div>
-                       <nav id="demosites_nav" class="collapse navbar-collapse navbar-left" role="navigation">
+                       <nav class="collapse navbar-collapse navbar-right" role="navigation">
                                <ul class="nav navbar-nav">
-                                       <li class="dropdown">
-                                               <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="current_view">demo sites <strong class="caret"></strong></a>
+                                       <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#optionsModal"><i class="fa fa-cog"></i> settings</a></li>
+                                       <li class="hidden-sm"><a href="https://github.com/firehol/netdata/wiki" class="btn" target="_blank"><i class="fa fa-github"></i> community</a></li>
+                                       <li class="hidden-sm" id="updateButton"><a href="#" class="btn" data-toggle="modal" data-target="#updateModal"><i class="fa fa-cloud-download"></i> update</a></li>
+                                       <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#helpModal"><i class="fa fa-question-circle"></i> help</a></li>
+                                       <li class="dropdown hidden-md hidden-lg hidden-xs">
+                                               <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="current_view">Menu <strong class="caret"></strong></a>
                                                <ul class="dropdown-menu scrollable-menu inpagemenu" role="menu">
-                                                       <li id="demo_eu"><a href="//netdata1.firehol.org?nowelcome">EU - London (DigitalOcean.com)</a></li>
-                                                       <li id="demo_us"><a href="//netdata2.firehol.org?nowelcome">US - Atlanta (CDN77.com)</a></li>
-                                                       <li id="demo_gr"><a href="//netdata3.firehol.org?nowelcome">EU - Greece</a></li>
-                                                       <li role="separator" class="divider"></li>
-                                                       <li id="demo_tv"><a href="tv.html">TV Dashboard for 2 servers</a></li>
-                                                       <li id="demosites"><a href="demosites.html">Dashboard for monitoring netdata demo sites</a></li>
+                                                       <li><a href="#" class="btn" data-toggle="modal" data-target="#optionsModal"><i class="fa fa-cog"></i> settings</a></li>
+                                                       <li><a href="https://github.com/firehol/netdata/wiki" class="btn" target="_blank"><i class="fa fa-github"></i> community</a></li>
+                                                       <li><a href="#" class="btn" data-toggle="modal" data-target="#helpModal"><i class="fa fa-question-circle"></i> help</a></li>
+                                               </ul>
+                                       </li>
+                                       <li class="dropdown hidden-sm hidden-md hidden-lg">
+                                               <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="current_view">my-netdata <strong class="caret"></strong></a>
+                                               <ul id="mynetdata_servers2" class="dropdown-menu scrollable-menu inpagemenu" role="menu">
                                                </ul>
                                        </li>
                                </ul>
                        </nav>
-                       <nav class="collapse navbar-collapse navbar-right" role="navigation">
-                               <ul class="nav navbar-nav">
-                                       <li><a href="#" class="btn" data-toggle="modal" data-target="#optionsModal"><i class="fa fa-cog"></i> settings</a></li>
-                                       <li><a href="https://github.com/firehol/netdata/wiki" class="btn" target="_blank"><i class="fa fa-github"></i> community</a></li>
-                                       <li id="updateButton"><a href="#" class="btn" data-toggle="modal" data-target="#updateModal"><i class="fa fa-cloud-download"></i> update</a></li>
-<!--                                   <li><a href="old/" class="btn" target="_blank"><i class="fa fa-step-backward"></i> old dashboard</a></li> -->
-                                       <li><a href="#" class="btn" data-toggle="modal" data-target="#helpModal"><i class="fa fa-question-circle"></i> help</a></li>
-<!--                                   <li><a href="#sec">Visualize</a></li>
-                                       <li><a href="#sec">Prototype</a></li>
--->                            </ul>
-                       </nav>
+       </nav>
                </div>
        </nav>
 
                </div>
        </div>
 
-<script>
+       <div class="modal fade" id="deleteRegistryModal" tabindex="-1" role="dialog" aria-labelledby="deleteRegistryModalLabel">
+               <div class="modal-dialog" role="document">
+                       <div class="modal-content">
+                               <div class="modal-header">
+                                       <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                                       <h4 class="modal-title" id="deleteRegistryModalLabel">Delete <span id="deleteRegistryServerName"></span>?</h4>
+                               </div>
+                               <div class="modal-body">
+                                       You are about to delete, from your personal list of netdata servers, the following server:
+                                       <p style="text-align: center; padding-top: 10px; padding-bottom: 10px; line-height: 2;">
+                                       <b><span id="deleteRegistryServerName2"></span></b>
+                                       <br/>
+                                       <b><span id="deleteRegistryServerURL"></span></b>
+                                       </p>
+                                       Are you sure you want to do this?
+                                       <br/>
+                                       <div style="padding: 10px;"></div>
+                                       <small>Keep in mind, this server will be added back if and when you visit it again.</small>
+                               </div>
+                               <div class="modal-footer">
+                                       <button type="button" class="btn btn-success" data-dismiss="modal">keep it</button>
+                                       <a href="#" onclick="notifyForDeleteRegistry(true);" type="button" class="btn btn-danger">delete it</a>
+                               </div>
+                       </div>
+               </div>
+       </div>
 
+<script>
 var this_is_demo = null;
 function isdemo() {
        if(this_is_demo !== null) return this_is_demo;
@@ -796,6 +898,12 @@ function isdemo() {
 
        try {
                if(typeof document.location.hostname === 'string') {
+                       if(document.location.hostname.endsWith('.firehol.org') ||
+                                       document.location.hostname.endsWith('.netdata.rocks') ||
+                                       document.location.hostname.endsWith('.netdata.online'))
+                                       this_is_demo = true;
+
+                       /*
                        if(document.location.hostname === 'netdata.firehol.org' || document.location.hostname === 'netdata1.firehol.org') {
                                document.getElementById("demo_eu").className = "active";
                                this_is_demo = true;
@@ -808,10 +916,8 @@ function isdemo() {
                                document.getElementById("demo_gr").className = "active";
                                this_is_demo = true;
                        }
+                       */
                }
-
-               if(!this_is_demo)
-                       document.getElementById("demosites_nav").style.visibility = "hidden";
        }
        catch(error) {
                ;
@@ -824,6 +930,28 @@ if(isdemo()) {
        document.getElementById('masthead').style.display = 'block';
 }
 
+var deleteRegistryGuid = null;
+function deleteRegistryModalHandler(guid, name, url) {
+       deleteRegistryGuid = guid;
+       document.getElementById('deleteRegistryServerName').innerHTML = name;
+       document.getElementById('deleteRegistryServerName2').innerHTML = name;
+       document.getElementById('deleteRegistryServerURL').innerHTML = url;
+       $('#deleteRegistryModal').modal('show');
+}
+
+function notifyForDeleteRegistry() {
+/*     if(deleteRegistryGuid) {
+               NETDATA.registry.delete(deleteRegistryGuid, function() {
+                       console.log('delete called callback');
+                       NETDATA.registry.init();
+               });
+       }
+
+       deleteRegistryGuid = null;
+       $('#deleteRegistryModal').modal('hide');
+*/
+}
+
 var options = {
        sparklines_registry: {},
        submenu_names: {},
@@ -2008,6 +2136,10 @@ function finalizePage() {
                notifyForUpdate(true);
        });
 
+       $('#deleteRegistryModal').on('hidden.bs.modal', function() {
+               deleteRegistryGuid = null;
+       });
+
        if(isdemo()) {
                if(!nowelcome) {
                        setTimeout(function() {