| (function() { |
| 'use strict'; |
| |
| var mesosApp = angular.module('mesos'); |
| |
| function hasSelectedText() { |
| if (window.getSelection) { // All browsers except IE before version 9. |
| var range = window.getSelection(); |
| return range.toString().length > 0; |
| } |
| return false; |
| } |
| |
| // Invokes the pailer for the specified host and path using the |
| // specified window_title. |
| function pailer(host, path, window_title) { |
| var url = 'http://' + host + '/files/read.json?path=' + path; |
| var pailer = |
| window.open('/static/pailer.html', url, 'width=580px, height=700px'); |
| |
| // Need to use window.onload instead of document.ready to make |
| // sure the title doesn't get overwritten. |
| pailer.onload = function() { |
| pailer.document.title = window_title + ' (' + host + ')'; |
| }; |
| } |
| |
| |
| function updateInterval(num_slaves) { |
| // TODO(bmahler): Increasing the update interval for large clusters |
| // is done purely to mitigate webui performance issues. Ideally we can |
| // keep a consistently fast rate for updating statistical information. |
| // For the full system state updates, it may make sense to break |
| // it up using pagination and/or splitting the endpoint. |
| if (num_slaves < 500) { |
| return 10000; |
| } else if (num_slaves < 1000) { |
| return 20000; |
| } else if (num_slaves < 5000) { |
| return 30000; |
| } else { |
| return 60000; |
| } |
| } |
| |
| |
| // Update the outermost scope with the new state. |
| function update($scope, $timeout, data) { |
| // Don't do anything if the data hasn't changed. |
| if ($scope.data == data) { |
| return true; // Continue polling. |
| } |
| |
| $scope.state = JSON.parse(data); |
| |
| // Determine if there is a leader (and redirect if not the leader). |
| if ($scope.state.leader) { |
| |
| // Redirect if we aren't the leader. |
| if ($scope.state.leader != $scope.state.pid) { |
| $scope.redirect = 6000; |
| $("#not-leader-alert").removeClass("hide"); |
| |
| var countdown = function() { |
| if ($scope.redirect == 0) { |
| // TODO(benh): Use '$window'. |
| window.location = '/master/redirect'; |
| } else { |
| $scope.redirect = $scope.redirect - 1000; |
| $timeout(countdown, 1000); |
| } |
| }; |
| countdown(); |
| return false; // Don't continue polling. |
| } |
| } |
| |
| // A cluster is named if the state returns a non-empty string name. |
| // Track whether this cluster is named in a Boolean for display purposes. |
| $scope.clusterNamed = !!$scope.state.cluster; |
| |
| // Check for selected text, and allow up to 20 seconds to pass before |
| // potentially wiping the user highlighted text. |
| // TODO(bmahler): This is to avoid the annoying loss of highlighting when |
| // the tables update. Once we can have tighter granularity control on the |
| // angular.js dynamic table updates, we should remove this hack. |
| $scope.time_since_update += $scope.delay; |
| |
| if (hasSelectedText() && $scope.time_since_update < 20000) { |
| return true; |
| } |
| |
| $scope.data = data; |
| |
| // Pass this pollTime to all relativeDate calls to make them all relative to |
| // the same moment in time. |
| // |
| // If relativeDate is called without a reference time, it instantiates a new |
| // Date to be the reference. Since there can be hundreds of dates on a given |
| // page, they would all be relative to slightly different moments in time. |
| $scope.pollTime = new Date(); |
| |
| // Update the maps. |
| $scope.slaves = {}; |
| $scope.frameworks = {}; |
| $scope.offers = {}; |
| $scope.completed_frameworks = {}; |
| $scope.active_tasks = []; |
| $scope.completed_tasks = []; |
| |
| // Update the stats. |
| $scope.cluster = $scope.state.cluster; |
| $scope.total_cpus = 0; |
| $scope.total_mem = 0; |
| $scope.used_cpus = 0; |
| $scope.used_mem = 0; |
| $scope.offered_cpus = 0; |
| $scope.offered_mem = 0; |
| |
| $scope.staged_tasks = $scope.state.staged_tasks; |
| $scope.started_tasks = $scope.state.started_tasks; |
| $scope.finished_tasks = $scope.state.finished_tasks; |
| $scope.killed_tasks = $scope.state.killed_tasks; |
| $scope.failed_tasks = $scope.state.failed_tasks; |
| $scope.lost_tasks = $scope.state.lost_tasks; |
| |
| $scope.activated_slaves = $scope.state.activated_slaves; |
| $scope.deactivated_slaves = $scope.state.deactivated_slaves; |
| |
| _.each($scope.state.slaves, function(slave) { |
| $scope.slaves[slave.id] = slave; |
| $scope.total_cpus += slave.resources.cpus; |
| $scope.total_mem += slave.resources.mem; |
| }); |
| |
| var setTaskMetadata = function(task) { |
| if (!task.executor_id) { |
| task.executor_id = task.id; |
| } |
| if (task.statuses.length > 0) { |
| task.start_time = task.statuses[0].timestamp * 1000; |
| task.finish_time = |
| task.statuses[task.statuses.length - 1].timestamp * 1000; |
| } |
| }; |
| |
| _.each($scope.state.frameworks, function(framework) { |
| $scope.frameworks[framework.id] = framework; |
| |
| _.each(framework.offers, function(offer) { |
| $scope.offers[offer.id] = offer; |
| $scope.offered_cpus += offer.resources.cpus; |
| $scope.offered_mem += offer.resources.mem; |
| offer.framework_name = $scope.frameworks[offer.framework_id].name; |
| offer.hostname = $scope.slaves[offer.slave_id].hostname; |
| }); |
| |
| $scope.used_cpus += framework.resources.cpus; |
| $scope.used_mem += framework.resources.mem; |
| |
| framework.cpus_share = 0; |
| if ($scope.total_cpus > 0) { |
| framework.cpus_share = framework.resources.cpus / $scope.total_cpus; |
| } |
| |
| framework.mem_share = 0; |
| if ($scope.total_mem > 0) { |
| framework.mem_share = framework.resources.mem / $scope.total_mem; |
| } |
| |
| framework.max_share = Math.max(framework.cpus_share, framework.mem_share); |
| |
| // If the executor ID is empty, this is a command executor with an |
| // internal executor ID generated from the task ID. |
| // TODO(brenden): Remove this once |
| // https://issues.apache.org/jira/browse/MESOS-527 is fixed. |
| _.each(framework.tasks, setTaskMetadata); |
| _.each(framework.completed_tasks, setTaskMetadata); |
| |
| $scope.active_tasks = $scope.active_tasks.concat(framework.tasks); |
| $scope.completed_tasks = |
| $scope.completed_tasks.concat(framework.completed_tasks); |
| }); |
| |
| _.each($scope.state.completed_frameworks, function(framework) { |
| $scope.completed_frameworks[framework.id] = framework; |
| |
| _.each(framework.completed_tasks, setTaskMetadata); |
| }); |
| |
| $scope.used_cpus -= $scope.offered_cpus; |
| $scope.used_mem -= $scope.offered_mem; |
| |
| $scope.idle_cpus = $scope.total_cpus - ($scope.offered_cpus + $scope.used_cpus); |
| $scope.idle_mem = $scope.total_mem - ($scope.offered_mem + $scope.used_mem); |
| |
| $scope.time_since_update = 0; |
| $scope.$broadcast('state_updated'); |
| |
| return true; // Continue polling. |
| } |
| |
| |
| // Main controller that can be used to handle "global" events. E.g.,: |
| // $scope.$on('$afterRouteChange', function() { ...; }); |
| // |
| // In addition, the MainCntl encapsulates the "view", allowing the |
| // active controller/view to easily access anything in scope (e.g., |
| // the state). |
| mesosApp.controller('MainCntl', [ |
| '$scope', '$http', '$location', '$timeout', '$modal', |
| function($scope, $http, $location, $timeout, $modal) { |
| $scope.doneLoading = true; |
| |
| // Adding bindings into scope so that they can be used from within |
| // AngularJS expressions. |
| $scope._ = _; |
| $scope.stringify = JSON.stringify; |
| $scope.encodeURIComponent = encodeURIComponent; |
| $scope.basename = function(path) { |
| // This is only a basic version of basename that handles the cases we care |
| // about, rather than duplicating unix basename functionality perfectly. |
| if (path === '/') { |
| return path; // Handle '/'. |
| } |
| |
| // Strip a trailing '/' if present. |
| if (path.length > 0 && path.lastIndexOf('/') === (path.length - 1)) { |
| path = path.substr(0, path.length - 1); |
| } |
| return path.substr(path.lastIndexOf('/') + 1); |
| }; |
| |
| $scope.$location = $location; |
| $scope.delay = 2000; |
| $scope.retry = 0; |
| $scope.time_since_update = 0; |
| |
| // Ordered Array of path => activeTab mappings. On successful route changes, |
| // the `pathRegexp` values are matched against the current route. The first |
| // match will be used to set the active navbar tab. |
| var NAVBAR_PATHS = [ |
| { |
| pathRegexp: /^\/slaves/, |
| tab: 'slaves' |
| }, |
| { |
| pathRegexp: /^\/frameworks/, |
| tab: 'frameworks' |
| }, |
| { |
| pathRegexp: /^\/offers/, |
| tab: 'offers' |
| } |
| ]; |
| |
| // Set the active tab on route changes according to NAVBAR_PATHS. |
| $scope.$on('$routeChangeSuccess', function(event, current) { |
| var path = current.$$route.originalPath; |
| |
| // Use _.some so the loop can exit on the first `pathRegexp` match. |
| var matched = _.some(NAVBAR_PATHS, function(nav) { |
| if (path.match(nav.pathRegexp)) { |
| $scope.navbarActiveTab = nav.tab; |
| return true; |
| } |
| }); |
| |
| if (!matched) $scope.navbarActiveTab = null; |
| }); |
| |
| var poll = function() { |
| $http.get('master/state.json', |
| {transformResponse: function(data) { return data; }}) |
| .success(function(data) { |
| if (update($scope, $timeout, data)) { |
| $scope.delay = updateInterval(_.size($scope.slaves)); |
| $timeout(poll, $scope.delay); |
| } |
| }) |
| .error(function() { |
| if ($scope.delay >= 128000) { |
| $scope.delay = 2000; |
| } else { |
| $scope.delay = $scope.delay * 2; |
| } |
| |
| var errorModal = $modal.open({ |
| controller: function($scope, $modalInstance, scope) { |
| // Give the modal reference to the root scope so it can access the |
| // `retry` variable. It needs to be passed by reference, not by |
| // value, since its value is changed outside the scope of the |
| // modal. |
| $scope.rootScope = scope; |
| }, |
| resolve: { |
| scope: function() { return $scope; } |
| }, |
| templateUrl: "template/dialog/masterGone.html" |
| }); |
| |
| // Make it such that everytime we hide the error-modal, we stop the |
| // countdown and restart the polling. |
| errorModal.result.then(function() { |
| if ($scope.countdown != null) { |
| if ($timeout.cancel($scope.countdown)) { |
| // Restart since they cancelled the countdown. |
| $scope.delay = 2000; |
| } |
| } |
| |
| // Start polling again, but do it asynchronously (and wait at |
| // least a second because otherwise the error-modal won't get |
| // properly shown). |
| $timeout(poll, 1000); |
| }); |
| |
| $scope.retry = $scope.delay; |
| var countdown = function() { |
| if ($scope.retry === 0) { |
| errorModal.close(); |
| } else { |
| $scope.retry = $scope.retry - 1000; |
| $scope.countdown = $timeout(countdown, 1000); |
| } |
| }; |
| countdown(); |
| }); |
| }; |
| |
| poll(); |
| }]); |
| |
| |
| mesosApp.controller('HomeCtrl', function($dialog, $scope) { |
| $scope.log = function($event) { |
| if (!$scope.state.log_dir) { |
| $dialog.messageBox( |
| 'Logging to a file is not enabled', |
| "Set the 'log_dir' option if you wish to access the logs.", |
| [{label: 'Continue'}] |
| ).open(); |
| } else { |
| pailer( |
| $scope.$location.host() + ':' + $scope.$location.port(), |
| '/master/log', |
| 'Mesos Master'); |
| } |
| }; |
| }); |
| |
| mesosApp.controller('FrameworksCtrl', function() {}); |
| |
| mesosApp.controller('OffersCtrl', function() {}); |
| |
| mesosApp.controller('FrameworkCtrl', function($scope, $routeParams) { |
| var update = function() { |
| if ($routeParams.id in $scope.completed_frameworks) { |
| $scope.framework = $scope.completed_frameworks[$routeParams.id]; |
| $scope.alert_message = 'This framework has terminated!'; |
| $('#alert').show(); |
| $('#framework').show(); |
| } else if ($routeParams.id in $scope.frameworks) { |
| $scope.framework = $scope.frameworks[$routeParams.id]; |
| $('#framework').show(); |
| } else { |
| $scope.alert_message = 'No framework found with ID: ' + $routeParams.id; |
| $('#alert').show(); |
| } |
| }; |
| |
| if ($scope.state) { |
| update(); |
| } |
| |
| var removeListener = $scope.$on('state_updated', update); |
| $scope.$on('$routeChangeStart', removeListener); |
| }); |
| |
| |
| mesosApp.controller('SlavesCtrl', function() {}); |
| |
| |
| mesosApp.controller('SlaveCtrl', [ |
| '$dialog', '$scope', '$routeParams', '$http', '$q', '$timeout', 'top', |
| function($dialog, $scope, $routeParams, $http, $q, $timeout, $top) { |
| $scope.slave_id = $routeParams.slave_id; |
| |
| var update = function() { |
| if (!($routeParams.slave_id in $scope.slaves)) { |
| $scope.alert_message = 'No slave found with ID: ' + $routeParams.slave_id; |
| $('#alert').show(); |
| return; |
| } |
| |
| var pid = $scope.slaves[$routeParams.slave_id].pid; |
| var hostname = $scope.slaves[$routeParams.slave_id].hostname; |
| var id = pid.substring(0, pid.indexOf('@')); |
| var host = hostname + ":" + pid.substring(pid.lastIndexOf(':') + 1); |
| |
| $scope.log = function($event) { |
| if (!$scope.state.log_dir) { |
| $dialog.messageBox( |
| 'Logging to a file is not enabled', |
| "Set the 'log_dir' option if you wish to access the logs.", |
| [{label: 'Continue'}] |
| ).open(); |
| } else { |
| pailer(host, '/slave/log', 'Mesos Slave'); |
| } |
| }; |
| |
| // Set up polling for the monitor if this is the first update. |
| if (!$top.started()) { |
| $top.start(host, $scope); |
| } |
| |
| $http.jsonp('http://' + host + '/' + id + '/state.json?jsonp=JSON_CALLBACK') |
| .success(function (response) { |
| $scope.state = response; |
| |
| $scope.slave = {}; |
| $scope.slave.frameworks = {}; |
| $scope.slave.completed_frameworks = {}; |
| |
| $scope.slave.staging_tasks = 0; |
| $scope.slave.starting_tasks = 0; |
| $scope.slave.running_tasks = 0; |
| |
| // Computes framework stats by setting new attributes on the 'framework' |
| // object. |
| function computeFrameworkStats(framework) { |
| framework.num_tasks = 0; |
| framework.cpus = 0; |
| framework.mem = 0; |
| |
| _.each(framework.executors, function(executor) { |
| framework.num_tasks += _.size(executor.tasks); |
| framework.cpus += executor.resources.cpus; |
| framework.mem += executor.resources.mem; |
| }); |
| } |
| |
| // Compute framework stats and update slave's mappings of those |
| // frameworks. |
| _.each($scope.state.frameworks, function(framework) { |
| $scope.slave.frameworks[framework.id] = framework; |
| computeFrameworkStats(framework); |
| }); |
| |
| _.each($scope.state.completed_frameworks, function(framework) { |
| $scope.slave.completed_frameworks[framework.id] = framework; |
| computeFrameworkStats(framework); |
| }); |
| |
| $('#slave').show(); |
| }) |
| .error(function(reason) { |
| $scope.alert_message = 'Failed to get slave usage / state: ' + reason; |
| $('#alert').show(); |
| }); |
| }; |
| |
| if ($scope.state) { |
| update(); |
| } |
| |
| var removeListener = $scope.$on('state_updated', update); |
| $scope.$on('$routeChangeStart', removeListener); |
| }]); |
| |
| |
| mesosApp.controller('SlaveFrameworkCtrl', [ |
| '$scope', '$routeParams', '$http', '$q', '$timeout', 'top', |
| function($scope, $routeParams, $http, $q, $timeout, $top) { |
| $scope.slave_id = $routeParams.slave_id; |
| $scope.framework_id = $routeParams.framework_id; |
| |
| var update = function() { |
| if (!($routeParams.slave_id in $scope.slaves)) { |
| $scope.alert_message = 'No slave found with ID: ' + $routeParams.slave_id; |
| $('#alert').show(); |
| return; |
| } |
| |
| var pid = $scope.slaves[$routeParams.slave_id].pid; |
| var hostname = $scope.slaves[$routeParams.slave_id].hostname; |
| var id = pid.substring(0, pid.indexOf('@')); |
| var host = hostname + ":" + pid.substring(pid.lastIndexOf(':') + 1); |
| |
| // Set up polling for the monitor if this is the first update. |
| if (!$top.started()) { |
| $top.start(host, $scope); |
| } |
| |
| $http.jsonp('http://' + host + '/' + id + '/state.json?jsonp=JSON_CALLBACK') |
| .success(function (response) { |
| $scope.state = response; |
| |
| $scope.slave = {}; |
| |
| function matchFramework(framework) { |
| return $scope.framework_id === framework.id; |
| } |
| |
| // Find the framework; it's either active or completed. |
| $scope.framework = |
| _.find($scope.state.frameworks, matchFramework) || |
| _.find($scope.state.completed_frameworks, matchFramework); |
| |
| if (!$scope.framework) { |
| $scope.alert_message = 'No framework found with ID: ' + $routeParams.framework_id; |
| $('#alert').show(); |
| return; |
| } |
| |
| // Compute the framework stats. |
| $scope.framework.num_tasks = 0; |
| $scope.framework.cpus = 0; |
| $scope.framework.mem = 0; |
| |
| _.each($scope.framework.executors, function(executor) { |
| $scope.framework.num_tasks += _.size(executor.tasks); |
| $scope.framework.cpus += executor.resources.cpus; |
| $scope.framework.mem += executor.resources.mem; |
| }); |
| |
| $('#slave').show(); |
| }) |
| .error(function (reason) { |
| $scope.alert_message = 'Failed to get slave usage / state: ' + reason; |
| $('#alert').show(); |
| }); |
| }; |
| |
| if ($scope.state) { |
| update(); |
| } |
| |
| var removeListener = $scope.$on('state_updated', update); |
| $scope.$on('$routeChangeStart', removeListener); |
| }]); |
| |
| |
| mesosApp.controller('SlaveExecutorCtrl', [ |
| '$scope', '$routeParams', '$http', '$q', '$timeout', 'top', |
| function($scope, $routeParams, $http, $q, $timeout, $top) { |
| $scope.slave_id = $routeParams.slave_id; |
| $scope.framework_id = $routeParams.framework_id; |
| $scope.executor_id = $routeParams.executor_id; |
| |
| var update = function() { |
| if (!($routeParams.slave_id in $scope.slaves)) { |
| $scope.alert_message = 'No slave found with ID: ' + $routeParams.slave_id; |
| $('#alert').show(); |
| return; |
| } |
| |
| var pid = $scope.slaves[$routeParams.slave_id].pid; |
| var hostname = $scope.slaves[$routeParams.slave_id].hostname; |
| var id = pid.substring(0, pid.indexOf('@')); |
| var host = hostname + ":" + pid.substring(pid.lastIndexOf(':') + 1); |
| |
| // Set up polling for the monitor if this is the first update. |
| if (!$top.started()) { |
| $top.start(host, $scope); |
| } |
| |
| $http.jsonp('http://' + host + '/' + id + '/state.json?jsonp=JSON_CALLBACK') |
| .success(function (response) { |
| $scope.state = response; |
| |
| $scope.slave = {}; |
| |
| function matchFramework(framework) { |
| return $scope.framework_id === framework.id; |
| } |
| |
| // Find the framework; it's either active or completed. |
| $scope.framework = |
| _.find($scope.state.frameworks, matchFramework) || |
| _.find($scope.state.completed_frameworks, matchFramework); |
| |
| if (!$scope.framework) { |
| $scope.alert_message = 'No framework found with ID: ' + $routeParams.framework_id; |
| $('#alert').show(); |
| return; |
| } |
| |
| function matchExecutor(executor) { |
| return $scope.executor_id === executor.id; |
| } |
| |
| // Look for the executor; it's either active or completed. |
| $scope.executor = |
| _.find($scope.framework.executors, matchExecutor) || |
| _.find($scope.framework.completed_executors, matchExecutor); |
| |
| if (!$scope.executor) { |
| $scope.alert_message = 'No executor found with ID: ' + $routeParams.executor_id; |
| $('#alert').show(); |
| return; |
| } |
| |
| $('#slave').show(); |
| }) |
| .error(function (reason) { |
| $scope.alert_message = 'Failed to get slave usage / state: ' + reason; |
| $('#alert').show(); |
| }); |
| }; |
| |
| if ($scope.state) { |
| update(); |
| } |
| |
| var removeListener = $scope.$on('state_updated', update); |
| $scope.$on('$routeChangeStart', removeListener); |
| }]); |
| |
| |
| // Reroutes a request like |
| // '/slaves/:slave_id/frameworks/:framework_id/executors/:executor_id/browse' |
| // to the executor's sandbox. This requires a second request because the |
| // directory to browse is known by the slave but not by the master. Request |
| // the directory from the slave, and then redirect to it. |
| // |
| // TODO(ssorallen): Add `executor.directory` to the state.json output so this |
| // controller of rerouting is no longer necessary. |
| mesosApp.controller('SlaveExecutorRerouterCtrl', |
| function($alert, $http, $location, $routeParams, $scope, $window) { |
| |
| function goBack(flashMessageOrOptions) { |
| if (flashMessageOrOptions) { |
| $alert.danger(flashMessageOrOptions); |
| } |
| |
| if ($window.history.length > 1) { |
| // If the browser has something in its history, just go back. |
| $window.history.back(); |
| } else { |
| // Otherwise navigate to the framework page, which is likely the |
| // previous page anyway. |
| $location.path('/frameworks/' + $routeParams.framework_id).replace(); |
| } |
| } |
| |
| // When navigating directly to this page, e.g. pasting the URL into the |
| // browser, the previous page is not a page in Mesos. In that case, navigate |
| // home. |
| if (!$scope.slaves) { |
| $alert.danger({ |
| message: "Navigate to the slave's sandbox via the Mesos UI.", |
| title: "Failed to find slaves." |
| }); |
| return $location.path('/').replace(); |
| } |
| |
| var slave = $scope.slaves[$routeParams.slave_id]; |
| |
| // If the slave doesn't exist, send the user back. |
| if (!slave) { |
| return goBack("Slave with ID '" + $routeParams.slave_id + "' does not exist."); |
| } |
| |
| var pid = slave.pid; |
| var hostname = $scope.slaves[$routeParams.slave_id].hostname; |
| var id = pid.substring(0, pid.indexOf('@')); |
| var port = pid.substring(pid.lastIndexOf(':') + 1); |
| var host = hostname + ":" + port; |
| |
| // Request slave details to get access to the route executor's "directory" |
| // to navigate directly to the executor's sandbox. |
| $http.jsonp('http://' + host + '/' + id + '/state.json?jsonp=JSON_CALLBACK') |
| .success(function(response) { |
| |
| function matchFramework(framework) { |
| return $routeParams.framework_id === framework.id; |
| } |
| |
| var framework = |
| _.find(response.frameworks, matchFramework) || |
| _.find(response.completed_frameworks, matchFramework); |
| |
| if (!framework) { |
| return goBack( |
| "Framework with ID '" + $routeParams.framework_id + |
| "' does not exist on slave with ID '" + $routeParams.slave_id + |
| "'." |
| ); |
| } |
| |
| function matchExecutor(executor) { |
| return $routeParams.executor_id === executor.id; |
| } |
| |
| var executor = |
| _.find(framework.executors, matchExecutor) || |
| _.find(framework.completed_executors, matchExecutor); |
| |
| if (!executor) { |
| return goBack( |
| "Executor with ID '" + $routeParams.executor_id + |
| "' does not exist on slave with ID '" + $routeParams.slave_id + |
| "'." |
| ); |
| } |
| |
| // Navigate to a path like '/slaves/:id/browse?path=%2Ftmp%2F', the |
| // recognized "browse" endpoint for a slave. |
| $location.path('/slaves/' + $routeParams.slave_id + '/browse') |
| .search({path: executor.directory}) |
| .replace(); |
| }) |
| .error(function(response) { |
| $alert.danger({ |
| bullets: [ |
| "The slave's hostname, '" + hostname + "', is not accessible from your network", |
| "The slave's port, '" + port + "', is not accessible from your network", |
| "The slave timed out or went offline" |
| ], |
| message: "Potential reasons:", |
| title: "Failed to connect to slave '" + $routeParams.slave_id + |
| "' on '" + host + "'." |
| }); |
| |
| // Is the slave dead? Navigate home since returning to the slave might |
| // end up in an endless loop. |
| $location.path('/').replace(); |
| }); |
| }); |
| |
| |
| mesosApp.controller('BrowseCtrl', function($scope, $routeParams, $http) { |
| var update = function() { |
| if ($routeParams.slave_id in $scope.slaves && $routeParams.path) { |
| $scope.slave_id = $routeParams.slave_id; |
| $scope.path = $routeParams.path; |
| |
| var pid = $scope.slaves[$routeParams.slave_id].pid; |
| var hostname = $scope.slaves[$routeParams.slave_id].hostname; |
| var id = pid.substring(0, pid.indexOf('@')); |
| var host = hostname + ":" + pid.substring(pid.lastIndexOf(':') + 1); |
| var url = 'http://' + host + '/files/browse.json?jsonp=JSON_CALLBACK'; |
| |
| $scope.slave_host = host; |
| |
| $scope.pail = function($event, path) { |
| pailer(host, path, decodeURIComponent(path)); |
| }; |
| |
| // TODO(bmahler): Try to get the error code / body in the error callback. |
| // This wasn't working with the current version of angular. |
| $http.jsonp(url, {params: {path: $routeParams.path}}) |
| .success(function(data) { |
| $scope.listing = data; |
| $('#listing').show(); |
| }) |
| .error(function() { |
| $scope.alert_message = 'Error browsing path: ' + $routeParams.path; |
| $('#alert').show(); |
| }); |
| } else { |
| if (!($routeParams.slave_id in $scope.slaves)) { |
| $scope.alert_message = 'No slave found with ID: ' + $routeParams.slave_id; |
| } else { |
| $scope.alert_message = 'Missing "path" request parameter.'; |
| } |
| $('#alert').show(); |
| } |
| }; |
| |
| if ($scope.state) { |
| update(); |
| } |
| |
| var removeListener = $scope.$on('state_updated', update); |
| $scope.$on('$routeChangeStart', removeListener); |
| }); |
| })(); |