Repository: ambari Updated Branches: refs/heads/trunk a69261890 -> 9986f6c15
AMBARI-6642. FE: Ambari installer wizard should use /recommendations API to determine component layout Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/9986f6c1 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/9986f6c1 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/9986f6c1 Branch: refs/heads/trunk Commit: 9986f6c158d1caddd8cb0d0a8d7fc06634a7bcbc Parents: a692618 Author: Srimanth Gunturi <[email protected]> Authored: Mon Jul 28 13:26:53 2014 -0700 Committer: Srimanth Gunturi <[email protected]> Committed: Mon Jul 28 14:50:44 2014 -0700 ---------------------------------------------------------------------- .../data/stacks/HDP-2.1/recommendations.json | 149 ++++++++++ ambari-web/app/controllers/installer.js | 7 + .../app/controllers/wizard/step5_controller.js | 280 +++++++++---------- .../app/controllers/wizard/step6_controller.js | 46 +-- .../app/models/stack_service_component.js | 29 -- ambari-web/app/routes/add_service_routes.js | 3 + ambari-web/app/routes/installer.js | 1 + ambari-web/app/utils/ajax/ajax.js | 14 + 8 files changed, 337 insertions(+), 192 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/9986f6c1/ambari-web/app/assets/data/stacks/HDP-2.1/recommendations.json ---------------------------------------------------------------------- diff --git a/ambari-web/app/assets/data/stacks/HDP-2.1/recommendations.json b/ambari-web/app/assets/data/stacks/HDP-2.1/recommendations.json new file mode 100644 index 0000000..1b851bf --- /dev/null +++ b/ambari-web/app/assets/data/stacks/HDP-2.1/recommendations.json @@ -0,0 +1,149 @@ +{ + "resources" : [ +{ + "Versions": { + "stack_name": "HDP", + "stack_version": "2.1.1" + }, + "hosts": ["dev1.hortonworks.com", "dev2.hortonworks.com", "dev3.hortonworks.com"], + "services": ["FALCON", "FLUME", "GANGLIA", "HBASE", "HCATALOG", "HDFS", "HIVE", "MAPREDUCE2", "NAGIOS", "OOZIE", "PIG", "SQOOP", "STORM", "TEZ", "WEBCHAT", "YARN", "ZOOKEEPER"], + "recommendations": { + "blueprint": { + "configurations": { + "global": { + "properties": { + "hbase_user": "hbase", + "clientPort": "2181", + "hadoop_heapsize": "1024" + } + }, + "core-site": { }, + "hdfs-site": { }, + "yarn-site": { }, + "hbase-site": { } + }, + "host_groups": [ + { + "name": "host-group-1", + "components": [ + { + "name": "NAMENODE" + }, + { + "name": "NAGIOS_SERVER" + }, + { + "name": "GANGLIA_SERVER" + }, + { + "name": "HBASE_MASTER" + }, + { + "name": "ZOOKEEPER_SERVER" + }, + { + "name": "DRPC_SERVER" + }, + { + "name": "NIMBUS" + }, + { + "name": "STORM_REST_API" + }, + { + "name": "STORM_UI_SERVER" + } + ] + }, + { + "name": "host-group-2", + "components": [ + { + "name": "SECONDARY_NAMENODE" + }, + { + "name": "HISTORYSERVER" + }, + { + "name": "APP_TIMELINE_SERVER" + }, + { + "name": "RESOURCEMANAGER" + }, + { + "name": "HIVE_METASTORE" + }, + { + "name": "HIVE_SERVER" + }, + { + "name": "WEBHCAT_SERVER" + }, + { + "name": "OOZIE_SERVER" + }, + { + "name": "ZOOKEEPER_SERVER" + }, + { + "name": "FALCON_SERVER" + } + ] + }, + { + "name": "host-group-3", + "components": [ + { + "name": "ZOOKEEPER_SERVER" + }, + { + "name": "DATANODE" + }, + { + "name": "NODEMANAGER" + }, + { + "name": "HBASE_REGIONSERVER" + }, + { + "name": "SUPERVISOR" + }, + { + "name": "CLIENT" + } + ] + } + ] + }, + "blueprint_cluster_binding": { + "host_groups": [ + { + "name": "host-group-1", + "hosts": [ + { + "fqdn": "hwa-cls2-m.viacode.com" + } + ] + }, + { + "name": "host-group-2", + "hosts": [ + { + "fqdn": "hwa-cls2-s1.viacode.com" + } + ] + }, + { + "name": "host-group-3", + "hosts": [ + { + "fqdn": "hwa-cls2-s2.viacode.com" + } + ] + } + ] + } + } +} +] +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/9986f6c1/ambari-web/app/controllers/installer.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/installer.js b/ambari-web/app/controllers/installer.js index a563ec2..6aa88c5 100644 --- a/ambari-web/app/controllers/installer.js +++ b/ambari-web/app/controllers/installer.js @@ -732,6 +732,13 @@ App.InstallerController = App.WizardController.extend({ var step = this.get('isStepDisabled').findProperty('step', i); step.set('value', true); } + }, + + /** + * Clear loaded recommendations + */ + clearRecommendations: function() { + this.set('recommendations', undefined) } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/9986f6c1/ambari-web/app/controllers/wizard/step5_controller.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/wizard/step5_controller.js b/ambari-web/app/controllers/wizard/step5_controller.js index 3368d5d..0bb077e 100644 --- a/ambari-web/app/controllers/wizard/step5_controller.js +++ b/ambari-web/app/controllers/wizard/step5_controller.js @@ -20,7 +20,6 @@ var App = require('app'); var numberUtils = require('utils/number_utils'); App.WizardStep5Controller = Em.Controller.extend({ - name: "wizardStep5Controller", /** @@ -215,11 +214,20 @@ App.WizardStep5Controller = Em.Controller.extend({ console.log("WizardStep5Controller: Loading step5: Assign Masters"); this.clearStep(); this.renderHostInfo(); - this.renderComponents(this.loadComponents()); - this.get('addableComponents').forEach(function (componentName) { - this.updateComponent(componentName); - }, this); - if (!this.get("selectedServicesMasters").filterProperty('isInstalled', false).length) { + this.loadComponentRecommendations(this.loadStepCallback); + }, + + /** + * Callback after load controller data (hosts, host components etc) + * @method loadStepCallback + */ + loadStepCallback: function(components, self) { + self.renderComponents(components); + + self.get('addableComponents').forEach(function (componentName) { + self.updateComponent(componentName); + }, self); + if (!self.get("selectedServicesMasters").filterProperty('isInstalled', false).length) { console.log('no master components to add'); App.router.send('next'); } @@ -289,81 +297,144 @@ App.WizardStep5Controller = Em.Controller.extend({ }, /** - * Load services info to appropriate variable and return masterComponentHosts + * Get recommendations info from API + * @return {undefined} + */ + loadComponentRecommendations: function(callback) { + var self = this; + + if (App.router.get('installerController.recommendations') !== undefined) { + // Don't do AJAX call if recommendations has been already received + // But if user returns to previous step (selecting services), stored recommendations will be cleared in routers' next handler and AJAX call will be made again + callback(self.createComponentInstalltationObjects(), self); + } else { + var selectedServices = App.StackService.find().filterProperty('isSelected').mapProperty('serviceName'); + var installedServices = App.StackService.find().filterProperty('isInstalled').mapProperty('serviceName'); + var services = installedServices.concat(selectedServices).uniq(); + + var hostNames = self.get('hosts').mapProperty('host_name'); + + return App.ajax.send({ + name: 'wizard.step5.recommendations', + sender: self, + data: { + stackVersionUrl: App.get('stackVersionURL'), + hosts: hostNames, + services: services + }, + success: 'loadRecommendationsSuccessCallback' + }). + retry({ + times: App.maxRetries, + timeout: App.timeout + }). + then(function () { + callback(self.createComponentInstalltationObjects(), self); + }, + function () { + App.showReloadPopup(); + console.log('Load recommendations failed'); + } + ); + } + }, + + /** + * Create components for displaying component-host comboboxes in UI assign dialog + * expects installerController.recommendations will be filled with recommendations API call result * @return {Object[]} */ - loadComponents: function () { - var selectedServices = App.StackService.find().filterProperty('isSelected').mapProperty('serviceName'); - var installedServices = App.StackService.find().filterProperty('isInstalled').mapProperty('serviceName'); - var services = installedServices.concat(selectedServices).uniq(); - var selectedNotInstalledServices = this.get('content.services').filterProperty('isSelected').filterProperty('isInstalled', false).mapProperty('serviceName'); + createComponentInstalltationObjects: function() { + var self = this; var masterComponents = []; - //get full list from mock data - if (this.get('isAddServiceWizard')) { + if (self.get('isAddServiceWizard')) { masterComponents = App.StackServiceComponent.find().filterProperty('isShownOnAddServiceAssignMasterPage'); } else { masterComponents = App.StackServiceComponent.find().filterProperty('isShownOnInstallerAssignMasterPage'); } - var masterHosts = this.get('content.masterComponentHosts'); //saved to local storage info - var resultComponents = []; + var masterHosts = self.get('content.masterComponentHosts'); //saved to local storage info + var selectedNotInstalledServices = self.get('content.services').filterProperty('isSelected').filterProperty('isInstalled', false).mapProperty('serviceName'); + var recommendations = App.router.get('installerController.recommendations'); - for (var index = 0; index < services.length; index++) { - var componentInfo = masterComponents.filterProperty('serviceName', services[index]); - // If service is already installed and not being added as a new service then render on UI only those master components - // that have already installed hostComponents. - // NOTE: On upgrade there might be a prior installed service with non-installed newly introduced serviceComponent - var isNotSelectedService = !selectedNotInstalledServices.contains(services[index]); - if (isNotSelectedService) { - componentInfo = componentInfo.filter(function (_component) { - return App.HostComponent.find().someProperty('componentName',_component.get('componentName')); + var resultComponents = []; + var multipleComponentHasBeenAdded = {}; + + recommendations.blueprint.host_groups.forEach(function(host_group) { + var hosts = recommendations.blueprint_cluster_binding.host_groups.findProperty('name', host_group.name).hosts; + + hosts.forEach(function(host) { + host_group.components.forEach(function(component) { + var willBeAdded = true; + var fullComponent = masterComponents.findProperty('componentName', component.name); + // If it's master component which should be shown + if (fullComponent) { + // If service is already installed and not being added as a new service then render on UI only those master components + // that have already installed hostComponents. + // NOTE: On upgrade there might be a prior installed service with non-installed newly introduced serviceComponent + var isNotSelectedService = !selectedNotInstalledServices.contains(fullComponent.get('serviceName')); + if (isNotSelectedService) { + willBeAdded = App.HostComponent.find().someProperty('componentName', component.name); + } + + if (willBeAdded) { + var savedComponents = masterHosts.filterProperty('component', component.name); + + if (self.get('multipleComponents').contains(component.name) && savedComponents.length > 0) { + if (!multipleComponentHasBeenAdded[component.name]) { + multipleComponentHasBeenAdded[component.name] = true; + + savedComponents.forEach(function(saved) { + resultComponents.push(self.createComponentInstalltationObject(fullComponent, host.fqdn, saved)); + }); + } + } else { + var savedComponent = masterHosts.findProperty('component', component.name); + resultComponents.push(self.createComponentInstalltationObject(fullComponent, host.fqdn, savedComponent)); + } + } + } }); - } + }); + }); + return resultComponents; + }, - componentInfo.forEach(function (_componentInfo) { - if (this.get('multipleComponents').contains(_componentInfo.get('componentName'))) { - var savedComponents = masterHosts.filterProperty('component', _componentInfo.get('componentName')); - if (savedComponents.length) { - savedComponents.forEach(function (item) { - var multipleMasterHost = {}; - multipleMasterHost.component_name = _componentInfo.get('componentName'); - multipleMasterHost.display_name = _componentInfo.get('displayName'); - multipleMasterHost.selectedHost = item.hostName; - multipleMasterHost.serviceId = services[index]; - multipleMasterHost.isInstalled = item.isInstalled; - multipleMasterHost.isServiceCoHost = false; - resultComponents.push(multipleMasterHost); - }) - } else { - var multipleMasterHosts = this.selectHost(_componentInfo.get('componentName')); - multipleMasterHosts.forEach(function (_host) { - var multipleMasterHost = {}; - multipleMasterHost.component_name = _componentInfo.get('componentName'); - multipleMasterHost.display_name = _componentInfo.get('displayName'); - multipleMasterHost.selectedHost = _host; - multipleMasterHost.serviceId = services[index]; - multipleMasterHost.isInstalled = false; - multipleMasterHost.isServiceCoHost = false; - resultComponents.push(multipleMasterHost); - }); + /** + * Create component for displaying component-host comboboxes in UI assign dialog + * @param fullComponent - full component description + * @param hostName - host fqdn where component will be installed + * @param savedComponent - the same object which function returns but created before + * @return {Object} + */ + createComponentInstalltationObject: function(fullComponent, hostName, savedComponent) { + var componentName = fullComponent.get('componentName'); - } - } else { - var savedComponent = masterHosts.findProperty('component', _componentInfo.get('componentName')); - var componentObj = {}; - componentObj.component_name = _componentInfo.get('componentName'); - componentObj.display_name = _componentInfo.get('displayName'); - componentObj.selectedHost = savedComponent ? savedComponent.hostName : this.selectHost(_componentInfo.get('componentName')); // call the method that plays selectNode algorithm or fetches from server - componentObj.isInstalled = savedComponent ? savedComponent.isInstalled : false; - componentObj.serviceId = services[index]; - componentObj.isServiceCoHost = App.StackServiceComponent.find().findProperty('componentName', _componentInfo.get('componentName')).get('isCoHostedComponent') && !this.get('isReassignWizard'); - resultComponents.push(componentObj); - } - }, this); + var componentObj = {}; + componentObj.component_name = componentName; + componentObj.display_name = fullComponent.get('displayName'); + componentObj.serviceId = fullComponent.get('serviceName'); + componentObj.isServiceCoHost = App.StackServiceComponent.find().findProperty('componentName', componentName).get('isCoHostedComponent') && !this.get('isReassignWizard'); + + if (savedComponent) { + componentObj.selectedHost = savedComponent.hostName; + componentObj.isInstalled = savedComponent.isInstalled; + } else { + componentObj.selectedHost = hostName; + componentObj.isInstalled = false; } - return resultComponents; + return componentObj; + }, + + /** + * Success-callback for recommendations request + * @param {object} data + * @method loadRecommendationsSuccessCallback + */ + loadRecommendationsSuccessCallback: function (data) { + App.router.set('installerController.recommendations', data.resources[0].recommendations); }, /** @@ -430,83 +501,6 @@ App.WizardStep5Controller = Em.Controller.extend({ }.observes('[email protected]'), /** - * select and return host for component by scheme - * Scheme is an object that has keys which compared to number of hosts, - * if key more that number of hosts, then return value of that key. - * Value is index of host in hosts array. - * - * @param {object} componentName - * @param {object} hosts - * @return {string} - * @method getHostForComponent - */ - getHostForComponent: function (componentName, hosts) { - var component = App.StackServiceComponent.find().findProperty('componentName', componentName); - if (component) { - var selectionScheme = App.StackServiceComponent.find().findProperty('componentName', componentName).get('selectionSchemeForMasterComponent'); - } else { - return hosts[0]; - } - - if (hosts.length === 1 || $.isEmptyObject(selectionScheme)) { - return hosts[0]; - } else { - for (var i in selectionScheme) { - if (window.isFinite(i)) { - if (hosts.length < window.parseInt(i)) { - return hosts[selectionScheme[i]]; - } - } - } - return hosts[selectionScheme['else']] - } - }, - - /** - * Get list of host names for master component with multiple instances - * @param {Object} component - * @param {Object} hosts - * @returns {string[]} - * @method getHostsForComponent - */ - getHostsForComponent: function (component, hosts) { - var defaultNoOfMasterHosts = component.get('defaultNoOfMasterHosts'); - var masterHosts = []; - if (hosts.length < defaultNoOfMasterHosts) { - defaultNoOfMasterHosts = hosts.length; - } - for (var index = 0; index < defaultNoOfMasterHosts; index++) { - masterHosts.push(hosts[index]); - } - return masterHosts; - }, - - /** - * Return hostName of masterNode for specified service - * @param componentName - * @return {string|string[]} - * @method selectHost - */ - selectHost: function (componentName) { - var component = App.StackServiceComponent.find().findProperty('componentName', componentName); - var hostNames = this.get('hosts').mapProperty('host_name'); - if (hostNames.length > 1 && App.StackServiceComponent.find().filterProperty('isNotPreferableOnAmbariServerHost').mapProperty('componentName').contains(componentName)) { - hostNames = this.get('hosts').mapProperty('host_name').filter(function (item) { - return item !== location.hostname; - }, this); - } - if (this.get('multipleComponents').contains(componentName)) { - if (component.get('defaultNoOfMasterHosts') > 1) { - return this.getHostsForComponent(component, hostNames); - } else { - return [this.getHostForComponent(componentName, hostNames)]; - } - } else { - return this.getHostForComponent(componentName, hostNames); - } - }, - - /** * On change callback for inputs * @param {string} componentName * @param {string} selectedHost http://git-wip-us.apache.org/repos/asf/ambari/blob/9986f6c1/ambari-web/app/controllers/wizard/step6_controller.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/wizard/step6_controller.js b/ambari-web/app/controllers/wizard/step6_controller.js index b70b1b7..ed1fe0a 100644 --- a/ambari-web/app/controllers/wizard/step6_controller.js +++ b/ambari-web/app/controllers/wizard/step6_controller.js @@ -345,30 +345,36 @@ App.WizardStep6Controller = Em.Controller.extend({ var headers = this.get('headers'); var slaveComponents = this.get('content.slaveComponentHosts'); if (!slaveComponents) { // we are at this page for the first time - var client_is_set = false; + var recommendations = App.router.get('installerController.recommendations'); + // Get all host-component pairs from recommendations + var componentHostPairs = recommendations.blueprint.host_groups.map(function(group) { + return group.components.map(function(component) { + return recommendations.blueprint_cluster_binding.host_groups.findProperty('name', group.name).hosts.map(function(host) { + return { component: component.name, host: host.fqdn}; + }); + }); + }); + + // Flatten results twice because of two map() call before + componentHostPairs = [].concat.apply([], componentHostPairs); + componentHostPairs = [].concat.apply([], componentHostPairs); + + var clientComponents = App.get('components.clients'); + + hostsObj.forEach(function (host) { var checkboxes = host.get('checkboxes'); - checkboxes.setEach('checked', !host.hasMaster); - checkboxes.setEach('isInstalled', false); - checkboxes.findProperty('title', headers.findProperty('name', 'CLIENT').get('label')).set('checked', false); - // First not Master should have Client (only first!) - if (!client_is_set) { - var dfs = App.StackService.find().findProperty('isPrimaryDFS'); - if (dfs.get('isSelected') || dfs.get('isInstalled')) { - var checkboxServiceComponent = checkboxes.findProperty('title', headers.findProperty('name', dfs.get('serviceComponents'). - findProperty('isShownOnInstallerSlaveClientPage').get('componentName')).get('label')); - if (checkboxServiceComponent && checkboxServiceComponent.get('checked')) { - checkboxes.findProperty('title', headers.findProperty('name', 'CLIENT').get('label')).set('checked', true); - client_is_set = true; + checkboxes.forEach(function(checkbox) { + var recommended = componentHostPairs.some(function(pair) { + var componentMatch = pair.component === checkbox.component; + if (checkbox.component === 'CLIENT' && !componentMatch) { + componentMatch = clientComponents.contains(pair.component); } - } - } + return pair.host === host.hostName && componentMatch; + }); + checkbox.checked = recommended; + }); }); - - if (this.get('isInstallerWizard') && hostsObj.everyProperty('hasMaster', true)) { - var lastHost = hostsObj[hostsObj.length - 1]; - lastHost.get('checkboxes').setEach('checked', true); - } } else { this.get('headers').forEach(function (header) { var nodes = slaveComponents.findProperty('componentName', header.get('name')); http://git-wip-us.apache.org/repos/asf/ambari/blob/9986f6c1/ambari-web/app/models/stack_service_component.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/models/stack_service_component.js b/ambari-web/app/models/stack_service_component.js index 8fe2ce0..511d756 100644 --- a/ambari-web/app/models/stack_service_component.js +++ b/ambari-web/app/models/stack_service_component.js @@ -192,10 +192,6 @@ App.StackServiceComponent = DS.Model.extend({ } }.property('componentName'), - selectionSchemeForMasterComponent: function() { - return App.StackServiceComponent.selectionScheme(this.get('componentName')); - }.property('componentName'), - /** @property {Boolean} coHostedComponents - components that are co-hosted with this component **/ coHostedComponents: function() { var componentName = this.get('componentName'); @@ -223,31 +219,6 @@ App.StackServiceComponent = DS.Model.extend({ App.StackServiceComponent.FIXTURES = []; -App.StackServiceComponent.selectionScheme = function (componentName){ - switch (componentName) { - case 'NAMENODE' : - return {"else": 0}; - case 'SECONDARY_NAMENODE' : - return {"else": 1}; - case 'HBASE_MASTER': - return {"6": 0, "31": 2, "else": 3}; - case 'JOBTRACKER': - case 'HISTORYSERVER': - case 'RESOURCEMANAGER': - case 'APP_TIMELINE_SERVER': - return {"31": 1, "else": 2}; - case 'OOZIE_SERVER': - case 'FALCON_SERVER' : - return {"6": 1, "31": 2, "else": 3}; - case 'HIVE_SERVER' : - case 'HIVE_METASTORE' : - case 'WEBHCAT_SERVER' : - return {"6": 1, "31": 2, "else": 4}; - default: - return {"else": 0}; - } -}; - App.StackServiceComponent.coHost = { 'HIVE_METASTORE': 'HIVE_SERVER', 'WEBHCAT_SERVER': 'HIVE_SERVER' http://git-wip-us.apache.org/repos/asf/ambari/blob/9986f6c1/ambari-web/app/routes/add_service_routes.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/routes/add_service_routes.js b/ambari-web/app/routes/add_service_routes.js index 51e21da..e1f0c96 100644 --- a/ambari-web/app/routes/add_service_routes.js +++ b/ambari-web/app/routes/add_service_routes.js @@ -113,6 +113,9 @@ module.exports = App.WizardRoute.extend({ addServiceController.saveServices(wizardStep4Controller); addServiceController.saveClients(wizardStep4Controller); addServiceController.setDBProperty('masterComponentHosts', undefined); + + var installerController = router.get('installerController'); + installerController.clearRecommendations(); // Force reload recommendation between steps 1 and 2 router.transitionTo('step2'); } }), http://git-wip-us.apache.org/repos/asf/ambari/blob/9986f6c1/ambari-web/app/routes/installer.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/routes/installer.js b/ambari-web/app/routes/installer.js index f16e81d..846b554 100644 --- a/ambari-web/app/routes/installer.js +++ b/ambari-web/app/routes/installer.js @@ -249,6 +249,7 @@ module.exports = Em.Route.extend({ controller.saveServices(wizardStep4Controller); controller.saveClients(wizardStep4Controller); + controller.clearRecommendations(); // Force reload recommendation between steps 4 and 5 controller.setDBProperty('masterComponentHosts', undefined); router.transitionTo('step5'); } http://git-wip-us.apache.org/repos/asf/ambari/blob/9986f6c1/ambari-web/app/utils/ajax/ajax.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/utils/ajax/ajax.js b/ambari-web/app/utils/ajax/ajax.js index bc969bd..584ed7b 100644 --- a/ambari-web/app/utils/ajax/ajax.js +++ b/ambari-web/app/utils/ajax/ajax.js @@ -1140,6 +1140,20 @@ var urls = { } }, + 'wizard.step5.recommendations': { + 'real': '{stackVersionUrl}/recommendations', + 'mock': '/data/stacks/HDP-2.1/recommendations.json', + 'type': 'POST', + 'format': function (data) { + return { + data: JSON.stringify({ + hosts: data.hosts, + services: data.services + }) + } + } + }, + 'preinstalled.checks': { 'real':'/requests', 'mock':'',
