diff --git a/.gitignore b/.gitignore index 77aadb72001..6695ac262e0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.ignore .classpath .project .settings diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RepoDefinitionEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RepoDefinitionEntity.java index d1fc9d8d601..edcc1f18e75 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RepoDefinitionEntity.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RepoDefinitionEntity.java @@ -37,6 +37,7 @@ import org.apache.ambari.server.state.RepositoryInfo; import org.apache.ambari.server.state.stack.RepoTag; + import com.google.common.base.Objects; /** diff --git a/ambari-server/src/main/resources/common-services/KAFKA/0.8.1/package/scripts/kafka_broker.py b/ambari-server/src/main/resources/common-services/KAFKA/0.8.1/package/scripts/kafka_broker.py index 2094a6f4f32..61ac5a5cb2c 100644 --- a/ambari-server/src/main/resources/common-services/KAFKA/0.8.1/package/scripts/kafka_broker.py +++ b/ambari-server/src/main/resources/common-services/KAFKA/0.8.1/package/scripts/kafka_broker.py @@ -1,4 +1,4 @@ -""" + """ Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information diff --git a/ambari-server/src/main/resources/common-services/ZEPPELIN/0.6.0.3.0/metainfo.xml b/ambari-server/src/main/resources/common-services/ZEPPELIN/0.6.0.3.0/metainfo.xml new file mode 100644 index 00000000000..597b6db42c9 --- /dev/null +++ b/ambari-server/src/main/resources/common-services/ZEPPELIN/0.6.0.3.0/metainfo.xml @@ -0,0 +1,103 @@ + + + + 2.0 + + + ZEPPELIN + Zeppelin Notebook + A web-based notebook that enables interactive data analytics. It enables you to + make beautiful data-driven, interactive and collaborative documents with SQL, Scala + and more. + + 0.6.0 + + + ZEPPELIN_MASTER + Zeppelin Notebook + MASTER + 1 + true + + + PYTHON + 10000 + + + + SPARK/SPARK_CLIENT + host + + true + + + + YARN/YARN_CLIENT + host + + true + + + + + + zeppelin + true + + + + + + + + any + + + zeppelin + + + + + + + + PYTHON + 300 + + + + HDFS + + + + zeppelin-config + zeppelin-env + zeppelin-shiro-ini + zeppelin-log4j-properties + + true + + + + quicklinks.json + true + + + + + diff --git a/ambari-web/app/app.js b/ambari-web/app/app.js index 11edd86c44d..c48a8b74ac6 100644 --- a/ambari-web/app/app.js +++ b/ambari-web/app/app.js @@ -371,7 +371,8 @@ module.exports = Em.Application.create({ addableMasterInstallerWizard: function () { return App.StackServiceComponent.find().filterProperty('isMasterAddableInstallerWizard').mapProperty('componentName') - }.property('App.router.clusterController.isLoaded'), + }.property(), + //.property('App.router.clusterController.isLoaded'), multipleMasters: function () { return App.StackServiceComponent.find().filterProperty('isMasterWithMultipleInstances').mapProperty('componentName') diff --git a/ambari-web/app/assets/data/users/privileges_admin.json b/ambari-web/app/assets/data/users/privileges_admin.json index 3c60252f001..d849d0bbaf5 100644 --- a/ambari-web/app/assets/data/users/privileges_admin.json +++ b/ambari-web/app/assets/data/users/privileges_admin.json @@ -3,11 +3,65 @@ "items" : [ { "href" : "http://c6401.ambari.apache.org:8080/api/v1/privileges/1", - "PrivilegeInfo" : { + "AuthorizationInfo" : { "permission_name" : "AMBARI.ADMINISTRATOR", "principal_name" : "admin", "principal_type" : "USER", - "privilege_id" : 1 + "authorization_id" : "AMBARI.ADMINISTRATOR" + } + }, + { + "href" : "http://c6401.ambari.apache.org:8080/api/v1/privileges/2", + "AuthorizationInfo" : { + "permission_name" : "CLUSTER.TOGGLE_KERBEROS", + "principal_name" : "admin", + "principal_type" : "USER", + "authorization_id" : "CLUSTER.TOGGLE_KERBEROS" + } + }, + { + "href" : "http://c6401.ambari.apache.org:8080/api/v1/privileges/3", + "AuthorizationInfo" : { + "permission_name" : "CLUSTER.MODIFY_CONFIGS", + "principal_name" : "admin", + "principal_type" : "USER", + "authorization_id" : "CLUSTER.MODIFY_CONFIGS" + } + }, + { + "href" : "http://c6401.ambari.apache.org:8080/api/v1/privileges/4", + "AuthorizationInfo" : { + "permission_name" : "SERVICE.START_STOP", + "principal_name" : "admin", + "principal_type" : "USER", + "authorization_id" : "SERVICE.START_STOP" + } + }, + { + "href" : "http://c6401.ambari.apache.org:8080/api/v1/privileges/5", + "AuthorizationInfo" : { + "permission_name" : "SERVICE.SET_SERVICE_USERS_GROUPS", + "principal_name" : "admin", + "principal_type" : "USER", + "authorization_id" : "SERVICE.SET_SERVICE_USERS_GROUPS" + } + }, + { + "href" : "http://c6401.ambari.apache.org:8080/api/v1/privileges/6", + "AuthorizationInfo" : { + "permission_name" : "CLUSTER.UPGRADE_DOWNGRADE_STACK", + "principal_name" : "admin", + "principal_type" : "USER", + "authorization_id" : "CLUSTER.UPGRADE_DOWNGRADE_STACK" + } + }, + { + "href" : "http://c6401.ambari.apache.org:8080/api/v1/privileges/7", + "AuthorizationInfo" : { + "permission_name" : "CLUSTER.VIEW_STACK_DETAILS", + "principal_name" : "admin", + "principal_type" : "USER", + "authorization_id" : "CLUSTER.VIEW_STACK_DETAILS" } } ] diff --git a/ambari-web/app/assets/index.html b/ambari-web/app/assets/index.html index efd3c644668..f55bbfe8574 100644 --- a/ambari-web/app/assets/index.html +++ b/ambari-web/app/assets/index.html @@ -29,10 +29,10 @@ Ambari diff --git a/ambari-web/app/assets/test/tests.js b/ambari-web/app/assets/test/tests.js index 51e9f3e2a63..06f78d8feca 100644 --- a/ambari-web/app/assets/test/tests.js +++ b/ambari-web/app/assets/test/tests.js @@ -97,6 +97,13 @@ var files = [ 'test/controllers/main/admin/highAvailability/journalNode/step8_controller_test', 'test/controllers/main/admin/highAvailability/journalNode/wizard_controller_test', 'test/controllers/main/admin/highAvailability/rangerAdmin/step3_controller_test', + 'test/controllers/main/admin/mpack_upgrade_controller_test', + 'test/controllers/main/admin/mpackUpgrade/downloadOptions_controller_test', + 'test/controllers/main/admin/mpackUpgrade/selectUpgradeOptions_controller_test', + 'test/controllers/main/admin/mpackUpgrade/downloadMpacks_controller_test', + 'test/controllers/main/admin/mpackUpgrade/reviewConfigs_controller_test', + 'test/controllers/main/admin/mpackUpgrade/selectUpgradeType_controller_test', + 'test/controllers/main/admin/mpackUpgrade/upgradeSummary_controller_test', 'test/controllers/main/dashboard/config_history_controller_test', 'test/controllers/main/charts/heatmap_test', 'test/controllers/main/charts/heatmap_metrics/heatmap_metric_test', @@ -135,6 +142,12 @@ var files = [ 'test/controllers/login_controller_test', 'test/controllers/experimental_test', 'test/controllers/wizard_test', + 'test/controllers/wizard/configureDownload_test', + 'test/controllers/wizard/selectMpacks_test', + 'test/controllers/wizard/downloadMpacks_test', + 'test/controllers/wizard/customProductRepos_test', + 'test/controllers/wizard/verifyProducts_test', + 'test/controllers/wizard/wizardStep_test', 'test/controllers/wizard/step0_test', 'test/controllers/wizard/step1_test', 'test/controllers/wizard/step2_test', @@ -246,6 +259,7 @@ var files = [ 'test/views/common/configs/widgets/list_config_widget_view_test', 'test/views/common/configs/widgets/slider_config_widget_view_test', 'test/views/common/configs/widgets/toggle_config_widget_view_test', + 'test/views/common/helpers/format_date_view_test', 'test/views/common/helpers/format_word_break_view_test', 'test/views/common/ajax_default_error_popup_body_test', 'test/views/common/filter_combo_cleanable_test', @@ -257,6 +271,8 @@ var files = [ 'test/views/common/modal_popup_test', 'test/views/common/sort_view_test', 'test/views/common/progress_bar_view_test', + 'test/views/common/dashrow_view_test', + 'test/views/common/timeline_view_test', 'test/views/common/select_custom_date_view_test', 'test/views/common/widget/graph_widget_view_test', 'test/views/common/widget/number_widget_view_test', @@ -385,6 +401,9 @@ var files = [ 'test/views/wizard/step3/hostLogPopupBody_view_test', 'test/views/wizard/step3/hostWarningPopupBody_view_test', 'test/views/wizard/step3/hostWarningPopupFooter_view_test', + 'test/views/wizard/configureDownload_view_test', + 'test/views/wizard/customMpackRepos_view_test', + 'test/views/wizard/customProductRepos_view_test', 'test/views/wizard/step0_view_test', 'test/views/wizard/step1_view_test', 'test/views/wizard/step2_view_test', diff --git a/ambari-web/app/controllers.js b/ambari-web/app/controllers.js index f7d77bedb7e..2598798bc94 100644 --- a/ambari-web/app/controllers.js +++ b/ambari-web/app/controllers.js @@ -86,6 +86,7 @@ require('controllers/main/admin/highAvailability/journalNode/step7_controller'); require('controllers/main/admin/highAvailability/journalNode/step8_controller'); require('controllers/main/admin/stack_and_upgrade_controller'); require('controllers/main/admin/stack_upgrade_history_controller'); +require('controllers/main/admin/serviceGroups_controller'); require('controllers/main/admin/serviceAccounts_controller'); require('utils/polling'); require('controllers/main/admin/kerberos'); @@ -100,6 +101,13 @@ require('controllers/main/admin/kerberos/step5_controller'); require('controllers/main/admin/kerberos/step6_controller'); require('controllers/main/admin/kerberos/step7_controller'); require('controllers/main/admin/kerberos/step8_controller'); +require('controllers/main/admin/mpack_upgrade_controller'); +require('controllers/main/admin/mpackUpgrade/downloadOptions_controller'); +require('controllers/main/admin/mpackUpgrade/downloadMpacks_controller'); +require('controllers/main/admin/mpackUpgrade/selectUpgradeOptions_controller'); +require('controllers/main/admin/mpackUpgrade/reviewConfigs_controller'); +require('controllers/main/admin/mpackUpgrade/selectUpgradeType_controller'); +require('controllers/main/admin/mpackUpgrade/upgradeSummary_controller'); require('controllers/main/alert_definitions_controller'); require('controllers/main/alerts/alert_definitions_actions_controller'); require('controllers/main/alerts/add_alert_definition/add_alert_definition_controller'); @@ -145,10 +153,17 @@ require('controllers/main/service/info/heatmap'); require('controllers/main/service/info/metric'); require('controllers/main/views_controller'); require('controllers/main/views/details_controller'); +require('controllers/wizard/wizardStep_controller'); require('controllers/wizard/step0_controller'); require('controllers/wizard/step1_controller'); require('controllers/wizard/step2_controller'); require('controllers/wizard/step3_controller'); +require('controllers/wizard/configureDownload_controller'); +require('controllers/wizard/selectMpacks_controller'); +require('controllers/wizard/customMpackRepos_controller'); +require('controllers/wizard/downloadMpacks_controller'); +require('controllers/wizard/customProductRepos_controller'); +require('controllers/wizard/verifyProducts_controller'); require('controllers/wizard/step4_controller'); require('controllers/wizard/step5_controller'); require('controllers/wizard/step6_controller'); diff --git a/ambari-web/app/controllers/installer.js b/ambari-web/app/controllers/installer.js index 63fcbf39394..f1761387bb8 100644 --- a/ambari-web/app/controllers/installer.js +++ b/ambari-web/app/controllers/installer.js @@ -41,8 +41,14 @@ App.InstallerController = App.WizardController.extend(App.Persist, { "step0", "step2", "step3", - "step1", - "step4", + "configureDownload", + "selectMpacks", + "customMpackRepos", + "downloadMpacks", + "customProductRepos", + "verifyProducts", + //"step1", + //"step4", "step5", "step6", "step7", @@ -51,6 +57,32 @@ App.InstallerController = App.WizardController.extend(App.Persist, { "step10" ], + errors: [], + + hasErrors: function () { + return this.get('errors').length > 0; + }.property('errors'), + + addError: function (newError) { + const errors = this.get('errors'); + this.set('errors', errors.concat(newError)); + }, + + clearErrors: function () { + this.set('errors', []); + }, + + getStepController: function (stepName) { + if (typeof (stepName) === "number") { + stepName = this.get('steps')[stepName]; + } + + stepName = stepName.charAt(0).toUpperCase() + stepName.slice(1); + const stepController = App.router.get('wizard' + stepName + 'Controller'); + + return stepController; + }, + content: Em.Object.create({ cluster: null, installOptions: null, @@ -76,7 +108,16 @@ App.InstallerController = App.WizardController.extend(App.Persist, { * (uses for host groups validation and to load recommended configs) */ recommendationsHostGroups: null, - controllerName: 'installerController' + controllerName: 'installerController', + mpacks: [], + mpackVersions: [], + mpackServiceVersions: [], + mpackServices: [], + // Tracks which steps have been saved before. + // If you revisit a step, we will know if the step has been saved previously and we can warn about making changes. + // If a previously saved step is changed, setStepSaved() will "unsave" all subsequent steps so we don't warn on every screen. + // Furthermore, we only need to track this state for steps that have an affect on subsequent steps. + stepsSavedState: null }), /** @@ -92,7 +133,7 @@ App.InstallerController = App.WizardController.extend(App.Persist, { 'installOptions', 'allHostNamesPattern', 'serviceComponents', - 'clientInfo', + 'clients', 'selectedServiceNames', 'serviceConfigGroups', 'serviceConfigProperties', @@ -107,7 +148,12 @@ App.InstallerController = App.WizardController.extend(App.Persist, { 'recommendationsConfigs', 'componentsFromConfigs', 'operatingSystems', - 'repositories' + 'repositories', + 'selectedMpacks', + 'selectedServices', + 'selectedStack', + 'downloadConfig', + 'stepsSavedState' ], init: function () { @@ -174,6 +220,87 @@ App.InstallerController = App.WizardController.extend(App.Persist, { return dfd.promise(); }, + /** + * Load data for stacks from selected mpacks. This just tells the server to populate the version_definitions endpoint for the mpack that was registered. + * + * @param {string} stackName + * @param {string} stackVersion + * @param {string} serviceName + */ + createMpackStackVersion: function (stackName, stackVersion) { + return App.ajax.send({ + name: 'mpack.create_version_definition', + sender: this, + data: { + name: stackName, + version: stackVersion + } + }) + }, + + /** + * Loads stack version data (including supported OSes and repos) from the version_definitions endpoint + */ + getMpackStackVersions: function () { + return App.ajax.send({ + name: 'mpack.get_version_definitions', + sender: this + }) + }, + + loadMpackStackInfoSuccess: function (versionDefinition) { + App.stackMapper.map(versionDefinition); + }, + + loadMpackStackInfoError: function(request, status, error) { + const message = Em.I18n.t('installer.error.mpackStackInfo'); + + App.showAlertPopup( + Em.I18n.t('common.error'), //header + message //body + ); + + console.log(`${message} ${status} - ${error}`); + }, + + /** + * Load data for services selected from mpacks. Will be used at Download Mpacks step submit action. + * + * @param {string} stackName + * @param {string} stackVersion + * @param {string} serviceName + */ + loadMpackServiceInfo: function (stackName, stackVersion, serviceName) { + return App.ajax.send({ + name: 'wizard.mpack_service_components', + sender: this, + data: { + stackName: stackName, + stackVersion: stackVersion, + serviceName: serviceName + } + }); + }, + + loadMpackServiceInfoSuccess: function (serviceInfo) { + serviceInfo.StackServices.is_selected = true; + App.MpackServiceMapper.map(serviceInfo); + }, + + loadMpackServiceInfoError: function(request, status, error) { + const message = Em.I18n.t('installer.error.mpackServiceInfo'); + + this.addError(message); + // App.showAlertPopup( + // Em.I18n.t('common.error'), //header + // message //body + // ); + + return message; + + console.log(`${message} ${status} - ${error}`); + }, + /** * total set of hosts registered to cluster, analog of App.Host model, * used in Installer wizard until hosts are installed @@ -509,11 +636,16 @@ App.InstallerController = App.WizardController.extend(App.Persist, { * Load master component hosts data for using in required step controllers * @param inMemory {Boolean}: Load master component hosts from memory */ - loadMasterComponentHosts: function (inMemory) { - var props = this.getDBProperties(['masterComponentHosts', 'hosts']); - var masterComponentHosts = !!inMemory ? this.get("content.masterComponentHosts") : props.masterComponentHosts, - hosts = props.hosts || {}, - hostNames = Em.keys(hosts); + loadMasterComponentHosts: function (lookInMemoryOnly) { + var props = this.getDBProperties(['masterComponentHosts', 'hosts']), + masterComponentHosts = this.get("content.masterComponentHosts"), + hosts = props.hosts || {}, + hostNames = Em.keys(hosts); + + if (!lookInMemoryOnly && !masterComponentHosts) { + masterComponentHosts = props.masterComponentHosts; + } + if (Em.isNone(masterComponentHosts)) { masterComponentHosts = []; } else { @@ -576,7 +708,7 @@ App.InstallerController = App.WizardController.extend(App.Persist, { }); }, this); }, this); - this.setDBProperty('clientInfo', clients); + this.setDBProperty('clients', clients); this.set('content.clients', clients); }, @@ -823,30 +955,26 @@ App.InstallerController = App.WizardController.extend(App.Persist, { prepareRepoForSaving: function(repo) { var repoVersion = { "operating_systems": [] }; var ambariManagedRepositories = !repo.get('useRedhatSatellite'); - var k = 0; - repo.get('operatingSystems').forEach(function (os) { - if (os.get('isSelected')) { - repoVersion.operating_systems.push({ - "OperatingSystems": { - "os_type": os.get("osType"), - "ambari_managed_repositories": ambariManagedRepositories - }, - "repositories": [] - }); - os.get('repositories').forEach(function (repository) { - repoVersion.operating_systems[k].repositories.push({ - "Repositories": { - "base_url": repository.get('baseUrl'), - "repo_id": repository.get('repoId'), - "repo_name": repository.get('repoName'), - "components": repository.get('components'), - "tags": repository.get('tags'), - "distribution": repository.get('distribution') - } - }); - }); - k++; - } + repo.get('operatingSystems').forEach(function (os, k) { + repoVersion.operating_systems.push({ + "OperatingSystems": { + "os_type": os.get("osType"), + "ambari_managed_repositories": ambariManagedRepositories + }, + "repositories": [] + }); + os.get('repositories').forEach(function (repository) { + repoVersion.operating_systems[k].repositories.push({ + "Repositories": { + "public_url": repository.get('baseUrlInit'), + "base_url": repository.get('baseUrl'), + "repo_id": repository.get('repoId'), + "repo_name": repository.get('repoName'), + "unique": repository.get('unique'), + "tags": repository.get('tags'), + } + }) + }) }); return repoVersion; }, @@ -953,63 +1081,50 @@ App.InstallerController = App.WizardController.extend(App.Persist, { { type: 'sync', callback: function () { + this.load('stepsSavedState'); this.load('cluster'); } } ], - 'step1': [ + 'step2': [ { - type: 'async', + type: 'sync', callback: function () { - var dfd = $.Deferred(); - - this.loadStacks().done(function(stacksLoaded) { - App.router.get('clusterController').loadAmbariProperties().always(function() { - dfd.resolve(stacksLoaded); - }); - }); - - return dfd.promise(); - } - }, - { - type: 'async', - callback: function (stacksLoaded) { - var dfd = $.Deferred(); - - if (!stacksLoaded) { - $.when.apply(this, this.loadStacksVersions()).done(function () { - dfd.resolve(true); - }); - } else { - dfd.resolve(stacksLoaded); - } - - return dfd.promise(); + this.load('installOptions'); } } ], - 'step2': [ + 'configureDownload': [ { type: 'sync', callback: function () { - this.load('installOptions'); + this.load('downloadConfig'); } - } + }, ], - 'step3': [ + 'selectMpacks': [ { type: 'sync', callback: function () { - this.loadConfirmedHosts(); + this.load('selectedServices'); + this.load('selectedMpacks'); + this.load('advancedMode'); } } ], - 'step4': [ + 'customProductRepos': [ { type: 'async', callback: function () { - return this.loadServices(); + return this.finishRegisteringMpacks(this.getStepSavedState('customProductRepos')); + } + }, + ], + 'step3': [ + { + type: 'sync', + callback: function () { + this.loadConfirmedHosts(); } } ], @@ -1017,7 +1132,7 @@ App.InstallerController = App.WizardController.extend(App.Persist, { { type: 'sync', callback: function () { - this.setSkipSlavesStep(App.StackService.find().filterProperty('isSelected'), 6); + this.setSkipSlavesStep(App.StackService.find().filterProperty('isSelected'), this.getStepIndex('step7')); this.loadMasterComponentHosts(); this.loadConfirmedHosts(); this.loadComponentsFromConfigs(); @@ -1053,6 +1168,14 @@ App.InstallerController = App.WizardController.extend(App.Persist, { return dfd.promise(); } } + ], + 'step8': [ + { + type: 'sync', + callback: function () { + this.load('selectedStack'); + } + } ] }, @@ -1115,6 +1238,30 @@ App.InstallerController = App.WizardController.extend(App.Persist, { this.gotoStep('step10'); }, + gotoConfigureDownload: function () { + this.gotoStep('configureDownload'); + }, + + gotoSelectMpacks: function () { + this.gotoStep('selectMpacks'); + }, + + gotoCustomMpackRepos: function () { + this.gotoStep('customMpackRepos'); + }, + + gotoDownloadMpacks: function () { + this.gotoStep('downloadMpacks'); + }, + + gotoCustomProductRepos: function () { + this.gotoStep('customProductRepos'); + }, + + gotoVerifyProducts: function () { + this.gotoStep('verifyProducts'); + }, + isStep0: function () { return this.get('currentStep') == this.getStepIndex('step0'); }.property('currentStep'), @@ -1159,6 +1306,30 @@ App.InstallerController = App.WizardController.extend(App.Persist, { return this.get('currentStep') == this.getStepIndex('step10'); }.property('currentStep'), + isConfigureDownload: function () { + return this.get('currentStep') == this.getStepIndex('configureDownload'); + }.property('currentStep'), + + isSelectMpacks: function () { + return this.get('currentStep') == this.getStepIndex('selectMpacks'); + }.property('currentStep'), + + isCustomMpackRepos: function () { + return this.get('currentStep') == this.getStepIndex('customMpackRepos'); + }.property('currentStep'), + + isDownloadMpacks: function () { + return this.get('currentStep') == this.getStepIndex('downloadMpacks'); + }.property('currentStep'), + + isCustomProductRepos: function () { + return this.get('currentStep') == this.getStepIndex('customProductRepos'); + }.property('currentStep'), + + isVerifyProducts: function () { + return this.get('currentStep') == this.getStepIndex('verifyProducts'); + }.property('currentStep'), + clearConfigActionComponents: function() { var masterComponentHosts = this.get('content.masterComponentHosts'); var componentsAddedFromConfigAction = this.get('content.componentsFromConfigs'); @@ -1199,11 +1370,19 @@ App.InstallerController = App.WizardController.extend(App.Persist, { setStepsEnable: function () { const steps = this.get('steps'); - for (var i = 0, length = steps.length; i < length; i++) { + for (let i = 0, length = steps.length; i < length; i++) { + let stepDisabled = true; + + const stepController = this.getStepController(steps[i]); + if (stepController) { + stepController.set('wizardController', this); + stepDisabled = stepController.isStepDisabled(); + } + const stepIndex = this.getStepIndex(steps[i]); - this.get('isStepDisabled').findProperty('step', stepIndex).set('value', stepIndex > this.get('currentStep')); + this.get('isStepDisabled').findProperty('step', stepIndex).set('value', stepDisabled); } - }.observes('currentStep'), + }, /** * Compare jdk versions used for ambari and selected stack. @@ -1244,6 +1423,140 @@ App.InstallerController = App.WizardController.extend(App.Persist, { } } sCallback(); - } + }, + + clearStackServices: function (deleteAll) { + var dfd = $.Deferred(); + if (deleteAll) { + const stackServices = App.StackService.find(); + let stackServicesCount = stackServices.content.length; + + if (stackServicesCount > 0) { + stackServices.forEach(service => { + Em.run.once(this, () => { + App.MpackServiceMapper.deleteRecord(service); + stackServicesCount--; + + if (stackServicesCount === 0) { + dfd.resolve(); + } + }); + }); + } else { + dfd.resolve(); + } + } else { + dfd.resolve(); + } + + return dfd.promise(); + }, + + getStepSavedState: function (stepName) { + const stepIndex = this.getStepIndex(stepName); + const stepsSaved = this.get('content.stepsSavedState'); + + if (!!stepIndex && stepsSaved && stepsSaved[stepIndex]) { + return true; + } + + return false; + }, + + setStepUnsaved: function (stepName) { + const stepIndex = this.getStepIndex(stepName); + const oldState = this.get('content.stepsSavedState') || {}; + const newState = Em.Object.create(oldState); + newState[stepIndex] = false; + + this.set('content.stepsSavedState', newState); + this.save('stepsSavedState'); + }, + + /** + * Updates the stepsSaved array based on the stepName provided. + * If the passed step is already saved, then nothing is changed. + * Otherwise, the passed step is set to saved and all subsequent steps are set to unsaved. + * + * @param {type} stepName Name of the step being saved. + */ + setStepSaved: function (stepName) { + const stepIndex = this.getStepIndex(stepName); + const oldState = this.get('content.stepsSavedState') || {}; + const newState = Em.Object.create(oldState); + + if (!newState[stepIndex]) { + for (let i = stepIndex + 1, length = this.get('steps').length; i < length; i++) { + newState[i] = false; + }; + + newState[stepIndex] = true; + + this.set('content.stepsSavedState', newState); + this.save('stepsSavedState'); + } + }, + + /** + * This runs when the step after Download Mpacks loads and completes the mpack registration process that was begun in the Download Mpacks step. + * It populates the StackService model from the stack version definitions. + * Then, it persists info about the selected services and the selected stack. + * + * @param {Boolean} keepStackServices If true, previously loaded stack services are retained. + * This is to support back/forward navigation in the wizard + * and should correspond to the saved state of the step after Download Mpacks. + * @return {object} a promise + */ + finishRegisteringMpacks: function (keepStackServices) { + var dfd = $.Deferred(); + + this.getMpackStackVersions() + .fail(errors => { + this.addErrors(errors); + dfd.reject(); + }) + .always(data => { + data.items.forEach(versionDefinition => App.stackMapper.map(versionDefinition)); + return this.clearStackServices(!keepStackServices); + }) + .then(() => { + //get info about services from specific stack versions and save to StackService model + const selectedServices = this.get('content.selectedServices'); + const servicePromises = selectedServices.map(service => + this.loadMpackServiceInfo(service.mpackName, service.mpackVersion, service.name) + .then(this.loadMpackServiceInfoSuccess.bind(this), this.loadMpackServiceInfoError.bind(this)) + ); + + return $.when(...servicePromises); + }) + .then(() => { + const services = App.StackService.find(); + this.set('content.services', services); + + const clients = []; + services.forEach(service => { + const client = service.get('serviceComponents').filterProperty('isClient', true); + client.forEach(clientComponent => { + clients.pushObject({ + component_name: clientComponent.get('componentName'), + display_name: clientComponent.get('displayName'), + isInstalled: false + }); + }); + }); + this.set('content.clients', clients); + this.save('clients'); + + //TODO: mpacks - hard coding this to use the name and version of the first stack/mpack for now. We need to get rid of the concept of "selected stack". + const stacks = App.Stack.find(); + const selectedStack = stacks.objectAtContent(0); + this.set('content.selectedStack', { name: selectedStack.get('stackName'), version: selectedStack.get('stackVersion') }); + this.save('selectedStack'); + + dfd.resolve(); + }); + + return dfd; + } }); diff --git a/ambari-web/app/controllers/main/admin/mpackUpgrade/downloadMpacks_controller.js b/ambari-web/app/controllers/main/admin/mpackUpgrade/downloadMpacks_controller.js new file mode 100644 index 00000000000..84094dd38bf --- /dev/null +++ b/ambari-web/app/controllers/main/admin/mpackUpgrade/downloadMpacks_controller.js @@ -0,0 +1,32 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +App.CreateUpgradePlanWizardDownloadMpacksController = App.WizardStepController.extend({ + + name: 'createUpgradePlanWizardDownloadMpacksController', + + stepName: 'downloadMpacks', + + submit: function () { + if (App.get('router.nextBtnClickInProgress')) { + return; + } + App.router.send('next'); + } + +}); \ No newline at end of file diff --git a/ambari-web/app/controllers/main/admin/mpackUpgrade/downloadOptions_controller.js b/ambari-web/app/controllers/main/admin/mpackUpgrade/downloadOptions_controller.js new file mode 100644 index 00000000000..1649e676b96 --- /dev/null +++ b/ambari-web/app/controllers/main/admin/mpackUpgrade/downloadOptions_controller.js @@ -0,0 +1,32 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +App.CreateUpgradePlanWizardDownloadOptionsController = App.WizardStepController.extend({ + + name: 'createUpgradePlanWizardDownloadOptionsController', + + stepName: 'downloadOptions', + + submit: function () { + if (App.get('router.nextBtnClickInProgress')) { + return; + } + App.router.send('next'); + } + +}); \ No newline at end of file diff --git a/ambari-web/app/controllers/main/admin/mpackUpgrade/reviewConfigs_controller.js b/ambari-web/app/controllers/main/admin/mpackUpgrade/reviewConfigs_controller.js new file mode 100644 index 00000000000..30e0891ddab --- /dev/null +++ b/ambari-web/app/controllers/main/admin/mpackUpgrade/reviewConfigs_controller.js @@ -0,0 +1,32 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +App.CreateUpgradePlanWizardReviewConfigsController = App.WizardStepController.extend({ + + name: 'createUpgradePlanWizardReviewConfigsController', + + stepName: 'reviewConfigs', + + submit: function () { + if (App.get('router.nextBtnClickInProgress')) { + return; + } + App.router.send('next'); + } + +}); \ No newline at end of file diff --git a/ambari-web/app/controllers/main/admin/mpackUpgrade/selectUpgradeOptions_controller.js b/ambari-web/app/controllers/main/admin/mpackUpgrade/selectUpgradeOptions_controller.js new file mode 100644 index 00000000000..90c09fd006d --- /dev/null +++ b/ambari-web/app/controllers/main/admin/mpackUpgrade/selectUpgradeOptions_controller.js @@ -0,0 +1,32 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +App.CreateUpgradePlanWizardSelectUpgradeOptionsController = App.WizardStepController.extend({ + + name: 'createUpgradePlanWizardSelectUpgradeOptionsController', + + stepName: 'selectUpgradeOptions', + + submit: function () { + if (App.get('router.nextBtnClickInProgress')) { + return; + } + App.router.send('next'); + } + +}); \ No newline at end of file diff --git a/ambari-web/app/controllers/main/admin/mpackUpgrade/selectUpgradeType_controller.js b/ambari-web/app/controllers/main/admin/mpackUpgrade/selectUpgradeType_controller.js new file mode 100644 index 00000000000..01390d86a5d --- /dev/null +++ b/ambari-web/app/controllers/main/admin/mpackUpgrade/selectUpgradeType_controller.js @@ -0,0 +1,32 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +App.CreateUpgradePlanWizardSelectUpgradeTypeController = App.WizardStepController.extend({ + + name: 'createUpgradePlanWizardSelectUpgradeTypeController', + + stepName: 'selectUpgradeType', + + submit: function () { + if (App.get('router.nextBtnClickInProgress')) { + return; + } + App.router.send('next'); + } + +}); \ No newline at end of file diff --git a/ambari-web/app/controllers/main/admin/mpackUpgrade/upgradeSummary_controller.js b/ambari-web/app/controllers/main/admin/mpackUpgrade/upgradeSummary_controller.js new file mode 100644 index 00000000000..c6987377a03 --- /dev/null +++ b/ambari-web/app/controllers/main/admin/mpackUpgrade/upgradeSummary_controller.js @@ -0,0 +1,32 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +App.CreateUpgradePlanWizardUpgradeSummaryController = App.WizardStepController.extend({ + + name: 'createUpgradePlanWizardUpgradeSummaryController', + + stepName: 'upgradeSummary', + + submit: function () { + if (App.get('router.nextBtnClickInProgress')) { + return; + } + App.router.send('next'); + } + +}); \ No newline at end of file diff --git a/ambari-web/app/controllers/main/admin/mpack_upgrade_controller.js b/ambari-web/app/controllers/main/admin/mpack_upgrade_controller.js new file mode 100644 index 00000000000..9c245a1ebf8 --- /dev/null +++ b/ambari-web/app/controllers/main/admin/mpack_upgrade_controller.js @@ -0,0 +1,176 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +var App = require('app'); + +App.CreateUpgradePlanWizardController = App.WizardController.extend({ + + name: 'createUpgradePlanWizardController', + + totalSteps: 6, + + currentStep: '0', + + steps: [ + "downloadOptions", + "selectUpgradeOptions", + "downloadMpacks", + "reviewConfigs", + "selectUpgradeType", + "upgradeSummary" + ], + + displayName: Em.I18n.t('admin.createUpgradePlan.wizard.header'), + + hideBackButton: false, + + //Add shared properties for the steps + content: Em.Object.create({ + controllerName: 'createUpgradePlanWizardController', + }), + + setCurrentStep: function (currentStep, completed) { + this._super(currentStep, completed); + App.clusterStatus.setClusterStatus({ + clusterName: this.get('content.cluster.name'), + clusterState: 'CREATE_UPGRADE_PLAN', + wizardControllerName: 'createUpgradePlanWizardController', + localdb: App.db.data + }); + }, + + //Define type and callback as per each step + loadMap: { + 'downloadOptions': [ + { + type: 'sync', + callback: function () { + + } + } + ], + 'selectUpgradeOptions': [ + { + type: 'sync', + callback: function () { + + } + } + ], + 'downloadMpacks': [ + { + type: 'sync', + callback: function () { + + } + } + ], + 'reviewConfigs': [ + { + type: 'sync', + callback: function () { + + } + } + ], + 'selectUpgradeType': [ + { + type: 'sync', + callback: function () { + + } + } + ], + 'upgradeSummary': [ + { + type: 'sync', + callback: function () { + + } + } + ] + }, + + + gotoDownloadOptions: function () { + this.gotoStep('downloadOptions'); + }, + + gotoSelectUpgradeOptions: function () { + this.gotoStep('selectUpgradeOptions'); + }, + + gotoDownloadMpacks: function () { + this.gotoStep('downloadMpacks'); + }, + + gotoReviewConfigs: function () { + this.gotoStep('reviewConfigs'); + }, + + gotoSelectUpgradeType: function () { + this.gotoStep('selectUpgradeType'); + }, + + gotoUpgradeSummary: function () { + this.gotoStep('upgradeSummary'); + }, + + isDownloadOptions: function () { + return this.get('currentStep') == this.getStepIndex('downloadOptions'); + }.property('currentStep'), + + isSelectUpgradeOptions: function () { + return this.get('currentStep') == this.getStepIndex('selectUpgradeOptions'); + }.property('currentStep'), + + isDownloadMpacks: function () { + return this.get('currentStep') == this.getStepIndex('downloadMpacks'); + }.property('currentStep'), + + isReviewConfigs: function () { + return this.get('currentStep') == this.getStepIndex('reviewConfigs'); + }.property('currentStep'), + + isSelectUpgradeType: function () { + return this.get('currentStep') == this.getStepIndex('selectUpgradeType'); + }.property('currentStep'), + + isSUpgradeSummary: function () { + return this.get('currentStep') == this.getStepIndex('upgradeSummary'); + }.property('currentStep'), + + + setStepsEnable: function () { + + for (var i = 0; i < this.get('steps').length; i++) { + var currentStep = this.get('currentStep'); + var step = this.get('isStepDisabled').findProperty('step', i); + var stepValue = i <= currentStep && App.get('router.clusterController.isLoaded') ? false : true; + step.set('value', stepValue); + } + }.observes('currentStep', 'App.router.clusterController.isLoaded'), + + finish: function () { + App.db.data.Installer = {}; + this.resetDbNamespace(); + App.router.get('updateController').updateAll(); + } + +}); \ No newline at end of file diff --git a/ambari-web/app/controllers/main/admin/serviceGroups_controller.js b/ambari-web/app/controllers/main/admin/serviceGroups_controller.js new file mode 100644 index 00000000000..d4ff2efba7b --- /dev/null +++ b/ambari-web/app/controllers/main/admin/serviceGroups_controller.js @@ -0,0 +1,253 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +App.MainAdminServiceGroupsController = Em.Controller.extend(App.LocalStorage, { + name: 'mainAdminServiceGroupsController', + + /** + * Dummy data that will be removed when we have an API to query. + */ + serviceGroups: [ + { + id: "HDPCore-300", + name: "HDP Core", + version: "3.0.0", + installedServices: [ + { + name: "HDFS", + version: "x.x" + }, + { + name: "Zookeeper", + version: "x.x" + } + ], + otherServices: [ + { + name: "Blarg", + version: "x.x" + }, + { + name: "Snarf", + version: "x.x" + } + ], + history: [ + { + date: new Date(2017, 0, 14), + version: '2.7', + versionType: 'release' + }, + { + date: new Date(2017, 12, 16), + version: '3.0', + versionType: 'release' + }, + { + date: new Date(2017, 9, 17), + version: '3.1', + versionType: 'release' + }, + { + date: new Date(2017, 0, 11), + version: '2.7', + versionType: 'release' + }, + { + date: new Date(2017, 12, 12), + version: '3.0', + versionType: 'release' + }, + { + date: new Date(2017, 9, 13), + version: '3.1', + versionType: 'release' + }, + { + date: new Date(2017, 6, 15), + version: '2.6.1.1', + versionType: 'hotfix' + }, + { + date: new Date(2017, 3, 15), + version: '2.6.2', + versionType: 'patch' + }, + { + date: new Date(2017, 0, 15), + version: '2.7', + versionType: 'release' + }, + { + date: new Date(2017, 12, 15), + version: '3.0', + versionType: 'release' + }, + { + date: new Date(2017, 9, 15), + version: '3.1', + versionType: 'release' + } + ] + }, + { + id: "ODS-300", + name: "ODS", + version: "3.0.0", + installedServices: [ + { + name: "HDFS", + version: "x.x" + }, + { + name: "Zookeeper", + version: "x.x" + } + ], + otherServices: [ + { + name: "Blarg", + version: "x.x" + }, + { + name: "Snarf", + version: "x.x" + } + ], + history: [ + { + date: new Date(2017, 6, 15), + version: '2.6.1.1', + versionType: 'hotfix' + }, + { + date: new Date(2017, 3, 15), + version: '2.6.2', + versionType: 'patch' + }, + { + date: new Date(2017, 0, 15), + version: '2.7', + versionType: 'release' + }, + { + date: new Date(2017, 9, 15), + version: '3.0', + versionType: 'release' + } + ] + } + ], + + // upgrade: null, + upgrade: { + id: "upgrade-1", + currentStep: "prerequisites", //can be "prerequisites", "install", or "upgrade" + //currentStep: "install", //can be "prerequisites", "install", or "upgrade" + //currentStep: "upgrade", //can be "prerequisites", "install", or "upgrade" + history: [ + { + name: "Event 1", + user: "Jason", + date: new Date(2018,1,1) + }, + { + name: "Event 2", + user: "Jason", + date: new Date(2018,2,1) + }, + { + name: "Event 3", + user: "Jason", + date: new Date(2017,3,1) + }, + { + name: "Event 4", + user: "Jason", + date: new Date(2017,4,1) + }, + { + name: "Event 5", + user: "Jason", + date: new Date(2017,5,1) + } + ], + mpacks: [ + { + name: "Hortonworks Data Platform Core", + currentVersion: "2.6", + newVersion: "2.7", + services: [ + { + name: "HDFS", + currentVersion: "2.6", + newVersion: "3.0" + }, + { + name: "Zookeeper", + currentVersion: "2.6", + newVersion: "3.0" + }, + { + name: "Zookeeper Client", + currentVersion: "2.6", + newVersion: "3.0" + }, + { + name: "MapReduce", + currentVersion: "2.6", + newVersion: "3.0" + } + ] + }, + { + name: "Data Science and Machine Learning", + currentVersion: "2.1", + newVersion: "2.2", + services: [ + { + name: "HDFS", + currentVersion: "2.6", + newVersion: "3.0" + }, + { + name: "Zookeeper", + currentVersion: "2.6", + newVersion: "3.0" + }, + { + name: "Zookeeper Client", + currentVersion: "2.6", + newVersion: "3.0" + }, + { + name: "MapReduce", + currentVersion: "2.6", + newVersion: "3.0" + } + ] + } + ] + }, + + createPlan: function () { }, + editPlan: function () { }, + discardPlan: function () {} +}); diff --git a/ambari-web/app/controllers/main/host/add_controller.js b/ambari-web/app/controllers/main/host/add_controller.js index 6b158c131ee..bd528f27712 100644 --- a/ambari-web/app/controllers/main/host/add_controller.js +++ b/ambari-web/app/controllers/main/host/add_controller.js @@ -223,7 +223,7 @@ App.AddHostController = App.WizardController.extend({ var serviceComponents = App.StackServiceComponent.find(); var services = this.get('content.services').filterProperty('isInstallable').filterProperty('isSelected'); var clients = this.getClientsToInstall(services, serviceComponents); - this.setDBProperty('clientInfo', clients); + this.setDBProperty('clients', clients); this.set('content.clients', clients); }, diff --git a/ambari-web/app/controllers/main/service/add_controller.js b/ambari-web/app/controllers/main/service/add_controller.js index c11bcf267a4..0399a483579 100644 --- a/ambari-web/app/controllers/main/service/add_controller.js +++ b/ambari-web/app/controllers/main/service/add_controller.js @@ -378,7 +378,7 @@ App.AddServiceController = App.WizardController.extend(App.AddSecurityConfigs, { }, this); }, this); - this.setDBProperty('clientInfo', clients); + this.setDBProperty('clients', clients); this.set('content.clients', clients); }, @@ -386,7 +386,7 @@ App.AddServiceController = App.WizardController.extend(App.AddSecurityConfigs, { * Load information about hosts with clients components */ loadClients: function () { - var clients = this.getDBProperty('clientInfo'); + var clients = this.getDBProperty('clients'); if (clients) { this.set('content.clients', clients); } else { diff --git a/ambari-web/app/controllers/wizard.js b/ambari-web/app/controllers/wizard.js index bc92ccf345d..f0948510f9e 100644 --- a/ambari-web/app/controllers/wizard.js +++ b/ambari-web/app/controllers/wizard.js @@ -228,6 +228,44 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM this.set('currentStep', index); }, + getPreviousStepName: function () { + const index = this.get('currentStep'); + + if (index > 0) { + const steps = this.get('steps'); + + if (steps) { + return steps[index - 1]; + } else { + //legacy support + return 'step' + (index - 1); + } + } else { + return null; + } + }, + + getNextStepName: function () { + const index = this.get('currentStep'); + + const steps = this.get('steps'); + if (steps) { + if (index < steps.length - 1) { + return steps[index + 1]; + } else { + return null + } + } + + //legacy support + const totalSteps = this.get('totalSteps'); + if (index < totalSteps - 1) { + return 'step' + (index + 1); + } else { + return null; + } + }, + clusters: null, isStep0: function () { @@ -319,17 +357,17 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM return false; } - if ((this.get('currentStep') - step) > 1 && !disableNaviWarning) { + if ((this.get('currentStep') - step) > 0 && !disableNaviWarning) { App.ModalPopup.show({ header: Em.I18n.t('installer.navigation.warning.header'), onPrimary: function () { - App.router.send('gotoStep' + step); + App.router.send('goto' + stepName.capitalize()); this.hide(); }, - body: "If you proceed to go back to Step " + step + ", you will lose any changes you have made beyond this step" + body: Em.I18n.t('installer.navigation.warning') }); } else { - App.router.send('gotoStep' + step); + App.router.send('goto' + stepName.capitalize()); } return true; @@ -621,6 +659,12 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM this.set('content.' + name, result); }, + + /** + * Save value from content to database. Converts Ember objects to plain objects first. + * + * @param {type} name + */ save: function (name) { var convertedValue = this.toJSInstance(this.get('content.' + name)); this.setDBProperty(name, convertedValue); @@ -660,8 +704,8 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM installOptionsTemplate: { hostNames: "", //string - manualInstall: false, //true, false - useSsh: true, //bool + manualInstall: true, //true, false + useSsh: false, //bool javaHome: App.defaultJavaHome, //string localRepo: false, //true, false sshKey: "", //string @@ -673,8 +717,8 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM installWindowsOptionsTemplate: { hostNames: "", //string - manualInstall: false, //true, false - useSsh: true, //bool + manualInstall: true, //true, false + useSsh: false, //bool javaHome: App.defaultJavaHome, //string localRepo: false, //true, false sshKey: "", //string @@ -1251,7 +1295,7 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM * Load information about hosts with clients components */ loadClients: function () { - var clients = this.getDBProperty('clientInfo'); + var clients = this.getDBProperty('clients'); this.set('content.clients', clients); }, @@ -1368,7 +1412,7 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM }, /** - * Determine if Assign Slaves and Clients step should be skipped + * Determine if Assign Slaves and Clients step ("step7") should be skipped * @method setSkipSlavesStep * @param services * @param step @@ -1564,6 +1608,20 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM this.setDBProperty('kerberosDescriptorConfigs', kerberosDescriptorConfigs); this.set('kerberosDescriptorConfigs', kerberosDescriptorConfigs); }, + + getStack: function (name, version) { + const stacks = App.Stack.find(); + + for (let i = 0, length = stacks.get('length'); i < length; i++) { + const stack = stacks.objectAt(i); + if (stack.get('stackName') === name && stack.get('stackVersion') === version) { + return stack; + } + } + + return null; + }, + /** * reset stored wizard data and reload App * @param {App.WizardController} controller - wizard controller diff --git a/ambari-web/app/controllers/wizard/configureDownload_controller.js b/ambari-web/app/controllers/wizard/configureDownload_controller.js new file mode 100644 index 00000000000..fc7c6302f23 --- /dev/null +++ b/ambari-web/app/controllers/wizard/configureDownload_controller.js @@ -0,0 +1,82 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); +require('./wizardStep_controller'); + +App.WizardConfigureDownloadController = App.WizardStepController.extend({ + + name: 'wizardConfigureDownloadController', + + stepName: 'configureDownload', + + loadStep: function () { + let downloadConfig = this.get('content.downloadConfig'); + if (!downloadConfig) { + this.set('content.downloadConfig', { + useRedHatSatellite: false, + useCustomRepo: false, + useProxy: false, + proxyUrl: null, + proxyAuth: null, + proxyTestPassed: false + }); + } + }, + + usePublicRepo: function () { + this.set('content.downloadConfig.useCustomRepo', false); + this.set('content.downloadConfig.useRedHatSatellite', false); + }, + + useCustomRepo: function () { + this.set('content.downloadConfig.useCustomRepo', true); + }, + + setProxyAuth: function (authType) { + this.set('content.downloadConfig.proxyAuth', authType); + this.proxySettingsChanged(); + }, + + proxySettingsChanged: function () { + this.set('content.downloadConfig.proxyTestPassed', false); + }, + + proxyTest: function () { + //TODO: mpacks - implement test proxy connection + this.set('content.downloadConfig.proxyTestPassed', true); + }, + + isSubmitDisabled: function () { + return App.get('router.btnClickInProgress') + || (this.get('wizardController.errors') && this.get('wizardController.errors').length > 0); + }.property('router.btnClickInProgress', 'wizardController.errors'), + + /** + * Onclick handler for Next button. + * Disable 'Next' button while it is already under process. (using Router's property 'nextBtnClickInProgress') + * @method submit + */ + submit: function () { + if (App.get('router.nextBtnClickInProgress')) { + return; + } + + App.router.send('next'); + } +}); diff --git a/ambari-web/app/controllers/wizard/customMpackRepos_controller.js b/ambari-web/app/controllers/wizard/customMpackRepos_controller.js new file mode 100644 index 00000000000..276881ebb07 --- /dev/null +++ b/ambari-web/app/controllers/wizard/customMpackRepos_controller.js @@ -0,0 +1,50 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +var App = require('app'); +require('./wizardStep_controller'); + +App.WizardCustomMpackReposController = App.WizardStepController.extend({ + + name: 'wizardCustomMpackReposController', + + stepName: 'customMpackRepos', + + mpacks: Em.computed.alias('content.selectedMpacks'), + + isStepDisabled: function (stepIndex, currentIndex) { + const normallyDisabled = this._super(stepIndex, currentIndex); + const useCustomRepo = this.get('wizardController.content.downloadConfig.useCustomRepo'); + + return normallyDisabled || !useCustomRepo; + }, + + isSubmitDisabled: function () { + const mpacks = this.get('mpacks'); + return App.get('router.btnClickInProgress') + || (this.get('wizardController.errors') && this.get('wizardController.errors').length > 0) + || mpacks.filterProperty('downloadUrl', '').length > 0; + }.property('mpacks.@each.downloadUrl', 'App.router.btnClickInProgress', 'wizardController.errors'), + + submit: function () { + if (App.get('router.nextBtnClickInProgress')) { + return; + } + + App.router.send('next'); + } +}); diff --git a/ambari-web/app/controllers/wizard/customProductRepos_controller.js b/ambari-web/app/controllers/wizard/customProductRepos_controller.js new file mode 100644 index 00000000000..3bb1385c318 --- /dev/null +++ b/ambari-web/app/controllers/wizard/customProductRepos_controller.js @@ -0,0 +1,285 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +var App = require('app'); +require('./wizardStep_controller'); + +App.WizardCustomProductReposController = App.WizardStepController.extend({ + + name: 'wizardCustomProductReposController', + + stepName: 'customProductRepos', + + stacks: [], + + mpacks: [], + + repos: [], + + operatingSystems: [], + + isOsSelected: function (type) { + const operatingSystems = this.get('operatingSystems'); + + if (operatingSystems && operatingSystems.length > 0) { + const os = operatingSystems.findProperty('type', type); + + if (os && os.get('selected')) { + return true; + } + } + + return false; + }, + + /** + * Pulls os and repo info from App.Stack and matches it up with selected mpacks. + * Adds the built up object to the mpacks array. + * Populates repos array. + * Populates operatingSystems array. + */ + loadStep: function () { + const selectedMpacks = this.get('content.selectedMpacks'); + const stacks = []; + + App.Stack.find().forEach(stack => { + const mpack = selectedMpacks.find(mpack => mpack.name === stack.get('stackName') && mpack.version === stack.get('stackVersion')); + + if (mpack) { + stacks.push(Em.Object.create({ + id: stack.get('id'), + name: mpack.name, + version: mpack.version, + operatingSystems: stack.get('operatingSystems').get('content').map((item, index) => { + const os = stack.get('operatingSystems').objectAtContent(index); + let selectedOs; + if (mpack.operatingSystems) { + selectedOs = mpack.operatingSystems.find(mpackOs => mpackOs.type === os.get('osType')); + } + + return Em.Object.create({ + type: os.get('osType'), + selected: selectedOs ? true : false, + isFirstSelected: false, + isLastSelected: false, + repos: os.get('repositories').get('content').map((item, index, repos) => { + const repo = os.get('repositories').objectAtContent(index); + let downloadUrl; + + if (selectedOs) { + const selectedRepo = selectedOs.repos.find(mpackRepo => mpackRepo.id === repo.get('repoId')); + if (selectedRepo) { + downloadUrl = selectedRepo.downloadUrl; + } + } + + return Em.Object.create({ + id: `${mpack.name}-${mpack.version}-${os.get('osType')}-${repo.get('repoId')}`, //this is a unique ID used in client logic + repoId: repo.get('repoId'), //this is the repo ID used by the server and displayed in the UI + name: repo.get('repoName'), + publicUrl: repo.get('baseUrlInit'), + downloadUrl: downloadUrl || repo.get('baseUrl'), + unique: repo.get('unique'), //this is a value that is only used by the server, but we need to preserve it + isFirst: index === 0, + isLast: index === repos.length - 1 + }); + }) + }); + }) + })); + } + }); + this.set('stacks', stacks); + + const mpacks = []; + selectedMpacks.forEach(mpack => { + const stack = stacks.find(stack => stack.get('name') === mpack.name && stack.get('version') === mpack.version); + mpacks.pushObject(Em.Object.create({ + id: stack.get('id'), //this is actually the stack id from App.Stack, which is actually the repository_version id in the database, which is an integer + name: mpack.name, + displayName: mpack.displayName, + publicUrl: mpack.publicUrl, + downloadUrl: mpack.downloadUrl, + version: mpack.version, + operatingSystems: stack ? stack.operatingSystems : [] + })); + }); + this.set('mpacks', mpacks); + + const repos = this.get('mpacks').reduce( + (repos, mpack) => repos.concat( + mpack.get('operatingSystems').reduce( + (repos, os) => repos.concat( + os.get('repos') + ), + []) + ), + [] + ); + this.set('repos', repos); + + const uniqueOperatingSystems = {}; + mpacks.forEach(mpack => { + mpack.get('operatingSystems').forEach(os => { + const osType = os.get('type'); + uniqueOperatingSystems[osType] + ? uniqueOperatingSystems[osType].mpacks.pushObject(mpack) + : uniqueOperatingSystems[osType] = { + selected: os.get('selected'), + mpacks: [mpack] + }; + }) + }); + + const operatingSystems = []; + for (let osType in uniqueOperatingSystems) { + operatingSystems.pushObject(Em.Object.create({ + type: osType, + selected: uniqueOperatingSystems[osType].selected, + mpacks: uniqueOperatingSystems[osType].mpacks + })) + } + operatingSystems.sort((a, b) => a.get('type').localeCompare(b.get('type'))); + this.set('operatingSystems', operatingSystems); + }, + + /** + * Returns the repo matching the given id. + * + * @param {string} repoId consisting of mpackName-mpackVersion-osType-repoId + */ + findRepoById: function (repoId) { + const mpacks = this.get('mpacks'); + + for (let mpack of mpacks) { + for (let os of mpack.operatingSystems) { + for (let repo of os.get('repos')) { + if (repo.get('id') === repoId) { + return repo; + } + } + } + } + }, + + toggleOs: function (osType) { + const os = this.get('operatingSystems').findProperty('type', osType); + + if (os) { + const mpacks = os.get('mpacks'); + const selected = os.get('selected'); + mpacks.forEach(mpack => { + const os = mpack.operatingSystems.findProperty('type', osType); + if (os) { + os.set('selected', selected); + } + }); + } + }, + + isStepDisabled: function (stepIndex, currentIndex) { + const normallyDisabled = this._super(stepIndex, currentIndex); + const useCustomRepo = this.get('wizardController.content.downloadConfig.useCustomRepo'); + + return normallyDisabled || !useCustomRepo; + }, + + anySelectedOs: function () { + const selectedOperatingSystems = this.get('operatingSystems').filterProperty('selected'); + return selectedOperatingSystems.length > 0; + }.property('operatingSystems.@each.selected'), + + isSubmitDisabled: function () { + if (this.get('anySelectedOs')) { + const repos = this.get('repos'); + return App.get('router.btnClickInProgress') + || (this.get('wizardController.errors') && this.get('wizardController.errors').length > 0) + || repos.filterProperty('downloadUrl', '').length > 0; + } + + return true; + }.property('anySelectedOs', 'repos.@each.downloadUrl', 'App.router.btnClickInProgress', 'wizardController.errors'), + + submit: function () { + if (App.get('router.nextBtnClickInProgress')) { + return; + } + + const mpacks = this.get('mpacks'); + + const selectedMpacks = mpacks.map(selectedMpack => + ({ + name: selectedMpack.name, + displayName: selectedMpack.displayName, + publicUrl: selectedMpack.publicUrl, + downloadUrl: selectedMpack.downloadUrl, + version: selectedMpack.version, + operatingSystems: selectedMpack.get('operatingSystems').filterProperty('selected').map(os => + ({ + type: os.get('type'), + selected: os.get('selected'), + isFirstSelected: os.get('isFirstSelected'), + isLastSelected: os.get('isLastSelected'), + repos: os.get('repos').map(repo => + ({ + id: repo.get('id'), + repoId: repo.get('repoId'), + downloadUrl: repo.get('downloadUrl'), + isFirst: repo.get('isFirst'), + isLast: repo.get('isLast') + }) + ) + }) + ) + }) + ); + this.set('content.selectedMpacks', selectedMpacks); + + const useRedHatSatellite = this.get('content.downloadConfig.useRedHatSatellite') + const updateRepoPromises = mpacks.map(mpack => { + const repoToUpdate = { + id: mpack.id, //this is actually the stack id from App.Stack, which is actually the repository_version id in the database, which is an integer + stackName: mpack.name, + stackVersion: mpack.version + } + + const repo = Em.Object.create({ + useRedhatSatellite: useRedHatSatellite, + operatingSystems: mpack.get('operatingSystems').map(os => + Em.Object.create({ + osType: os.type, + repositories: os.get('repos').map(repo => + Em.Object.create({ + baseUrlInit: repo.get('publicUrl'), + baseUrl: repo.get('downloadUrl'), + repoId: repo.get('repoId'), + repoName: repo.get('name'), + unique: repo.get('unique') //this is a value that is only used by the server, but we need to preserve it + }) + ) + }) + ) + }); + + this.get('wizardController').updateRepoOSInfo(repoToUpdate, repo) + }); + + $.when(...updateRepoPromises).then(() => { + App.router.send('next'); + }); + } +}); diff --git a/ambari-web/app/controllers/wizard/downloadMpacks_controller.js b/ambari-web/app/controllers/wizard/downloadMpacks_controller.js new file mode 100644 index 00000000000..08e85c14f2f --- /dev/null +++ b/ambari-web/app/controllers/wizard/downloadMpacks_controller.js @@ -0,0 +1,157 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +var App = require('app'); +require('./wizardStep_controller'); + +App.WizardDownloadMpacksController = App.WizardStepController.extend({ + + name: 'wizardDownloadMpacksController', + + stepName: 'downloadMpacks', + + mpacks: [], + + addMpacks: function () { + const selectedMpacks = this.get('content.selectedMpacks'); + + selectedMpacks.forEach(mpack => { + this.get('mpacks').pushObject(Em.Object.create({ + name: mpack.name, + displayName: mpack.displayName, + url: mpack.downloadUrl, + inProgress: true, + failed: false, + succeeded: false, + failureMessage: null + })); + }, this); + }, + + registerMpacks: function () { + var mpacks = this.get('mpacks'); + var self = this; + mpacks.forEach(function (mpack) { + self.downloadMpack(mpack); + }); + }, + + downloadMpack: function (mpack) { + console.log("downloading mpacks"); + App.ajax.send({ + name: 'mpack.download_by_url', + sender: this, + data: { + name: mpack.name, + url: mpack.url + }, + success: 'downloadMpackSuccess', + error: 'downloadMpackError', + }); + }, + + downloadMpackSuccess: function (data, opt, params) { + this.get('mpacks').findProperty('name', params.name).set('succeeded', true); + this.get('mpacks').findProperty('name', params.name).set('failed', false); + this.get('mpacks').findProperty('name', params.name).set('inProgress', false); + }, + + downloadMpackError: function (request, ajaxOptions, error, opt, params) { + if(request.status == 409) { + this.downloadMpackSuccess(request, opt, params); + } else { + this.get('mpacks').findProperty('name', params.name).set('succeeded', false); + this.get('mpacks').findProperty('name', params.name).set('failed', true); + this.get('mpacks').findProperty('name', params.name).set('inProgress', false); + + let failureMessage; + switch (request.status) { + case 400: + case 500: + failureMessage = request.statusText; + break; + default: + failureMessage = Em.i18n.t('installer.downloadMpacks.failure.default'); + } + + this.get('mpacks').findProperty('name', params.name).set('failureMessage', failureMessage); + } + }, + + retryDownload: function (event) { + const mpack = event.context; + + if (mpack.get('failed')) { + mpack.set('inProgress', true); + mpack.set('succeeded', false); + mpack.set('failed', false); + this.downloadMpack(mpack); + } + }, + + showError: function (event) { + const mpack = event.context; + + if (mpack.get('failed')) { + const error = mpack.get('failureMessage'); + + App.ModalPopup.show({ + header: `${Em.I18n.t('common.download')} ${Em.I18n.t('common.failed')}`, + primary: Em.I18n.t('common.close'), + secondary: false, + body: error + }); + } + }, + + getRegisteredMpacks: function () { + return App.ajax.send({ + name: 'mpack.get_registered_mpacks', + sender: this + }); + }, + + isSubmitDisabled: function () { + const mpacks = this.get('mpacks'); + return App.get('router.btnClickInProgress') + || (this.get('wizardController.errors') && this.get('wizardController.errors').length > 0) + || mpacks.filterProperty('succeeded', false).length > 0; + }.property('mpacks.@each.succeeded', 'App.router.btnClickInProgress', 'wizardController.errors'), + + submit: function () { + if (App.get('router.nextBtnClickInProgress')) { + return; + } + + if (!this.get('isSubmitDisabled')) { + //get info about stacks from version definitions and save to Stack model + this.getRegisteredMpacks().then(mpacks => { + const stackVersionsRegistered = mpacks.items.map(mpack => this.get('wizardController').createMpackStackVersion + ( + mpack.version[0].Versions.stack_name, + mpack.version[0].Versions.stack_version + ) + ); + + $.when(...stackVersionsRegistered).always(() => { //this uses always() because the api call made by createMpackStackVersion will return a 500 error + //if the stack version has already been registered, but we want to proceed anyway + App.router.send('next'); + }); + }); + } + } +}); diff --git a/ambari-web/app/controllers/wizard/selectMpacks_controller.js b/ambari-web/app/controllers/wizard/selectMpacks_controller.js new file mode 100644 index 00000000000..3c6743063ba --- /dev/null +++ b/ambari-web/app/controllers/wizard/selectMpacks_controller.js @@ -0,0 +1,612 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); +require('./wizardStep_controller'); + +App.WizardSelectMpacksController = App.WizardStepController.extend({ + + name: 'wizardSelectMpacksController', + + stepName: 'selectMpacks', + + noRecommendationAvailable: false, + + filterMpacksText: "", + + filterServicesText: "", + + filterMpacksPlaceholder: Em.I18n.t('installer.selectMpacks.filterMpacks'), + + filterServicesPlaceholder: Em.I18n.t('installer.selectMpacks.filterServices'), + + loadRegistry: function () { + return App.ajax.send({ + name: 'registry.all', + showLoadingPopup: true, + sender: this + }); + }, + + loadRegistrySucceeded: function (data) { + const mpacks = data.items.reduce( + (mpacks, registry) => mpacks.concat( + registry.mpacks.map(mpack => { + return Em.Object.create({ + name: mpack.RegistryMpackInfo.mpack_name, + displayName: mpack.RegistryMpackInfo.mpack_display_name, + description: mpack.RegistryMpackInfo.mpack_description, + //this is the text that will be used to filter this mpack in the UI + //at this point, this is just the text that comes from this mpack + //but additional text will be appended to form the final filterOn value + //from the mpack's services, specifically the service name + filterOn: ( + (mpack.RegistryMpackInfo.mpack_name || "") + " " + + (mpack.RegistryMpackInfo.mpack_display_name || "") + " " + + (mpack.RegistryMpackInfo.mpack_description || "") + ).toLowerCase(), + logoUrl: mpack.RegistryMpackInfo.mpack_logo_url, + versions: mpack.versions ? mpack.versions.map((version, index) => { + return Em.Object.create({ + selected: false, + displayed: index === 0 ? true : false, //by default, display first version + id: mpack.RegistryMpackInfo.mpack_name + version.RegistryMpackVersionInfo.mpack_version, + version: version.RegistryMpackVersionInfo.mpack_version, + mpackUrl: version.RegistryMpackVersionInfo.mpack_uri, + logoUrl: version.RegistryMpackVersionInfo.mpack_logo_uri, + docUrl: version.RegistryMpackVersionInfo.mpack_doc_uri, + services: version.RegistryMpackVersionInfo.modules ? version.RegistryMpackVersionInfo.modules.map(service => { + return Em.Object.create({ + selected: false, + displayed: index === 0 ? true : false, //by default, display first version + id: mpack.RegistryMpackInfo.mpack_name + version.RegistryMpackVersionInfo.mpack_version + service.name, + name: service.name, + displayName: service.displayName, + version: service.version + }) + }) : [] + }) + }) : [] + }) + }) + ), [] + ); + + const mpackVersions = mpacks.reduce( + (versions, mpack) => versions.concat( + mpack.get('versions').map(version => { + version.set('mpack', mpack); + return version; + }) + ), + [] + ); + + const mpackServiceVersions = mpackVersions.reduce( + (services, mpackVersion) => services.concat( + mpackVersion.get('services').map(service => { + service.set('mpackVersion', mpackVersion); + return service; + }) + ), + [] + ); + + const uniqueServices = {}; + mpackServiceVersions.forEach(service => { + //append service name to filterOn of the containing mpack + const mpackFilterOn = service.get('mpackVersion.mpack.filterOn'); + service.set('mpackVersion.mpack.filterOn', ((mpackFilterOn) + " " + (service.name || "")).toLowerCase()); + + uniqueServices[service.name] = Em.Object.create({ + name: service.name, + displayName: service.displayName, + description: service.description, + filterOn: ( + (service.name || "") + " " + + (service.description || "") + " " + + (service.get('mpackVersion.mpack.displayName') || "" ) + ).toLowerCase(), + displayedVersion: function () { + return this.get('versions').filterProperty('displayed')[0]; + }.property('versions.@each.displayed') + }) + }); + + const mpackServices = []; + for (let serviceName in uniqueServices) { + const service = uniqueServices[serviceName]; + const versions = mpackServiceVersions.filter(serviceVersion => serviceVersion.get('name') === service.name).map(serviceVersion => { + serviceVersion.set('service', service); + return serviceVersion; + }) + + service.set('versions', versions); + mpackServices.push(service); + } + + this.set('content.mpacks', mpacks); + this.set('content.mpackVersions', mpackVersions); + this.set('content.mpackServiceVersions', mpackServiceVersions); + this.set('content.mpackServices', mpackServices); + + const usecases = data.items.reduce( + (usecases, registry) => usecases.concat( + registry.scenarios.map(usecase => { + return Em.Object.create({ + selected: false, + id: usecase.RegistryScenarioInfo.scenario_id || usecase.RegistryScenarioInfo.scenario_name, //TODO: mpacks - remove fallback when id is available + name: usecase.RegistryScenarioInfo.scenario_name, + displayName: usecase.RegistryScenarioInfo.scenario_display_name || usecase.RegistryScenarioInfo.scenario_name, //TODO: mpacks - remove fallback when display name is available + description: usecase.RegistryScenarioInfo.scenario_description, + mpacks: this.getMpacksByName(usecase.RegistryScenarioInfo.scenario_mpacks.map(mpack => mpack.name)) + }); + }) + ), [] + ); + + this.set('content.mpackUsecases', usecases); + }, + + getMpacksByName: function (mpackNames) { + return mpackNames.map(mpackName => this.getMpackByName(mpackName)); + }, + + /** + * Returns the first (newest) version of the mpack with name matching mpackName. + * + * @param {string} mpackName + * @returns mpackVersion + */ + getMpackByName: function (mpackName) { + const mpacks = this.get('content.mpacks'); + + if (mpacks) { + //reinstate this if/when the test runner can handle for..of loops + //for (let mpack of mpacks) { + //if (mpack.get('name') === mpackName) { + // return mpack.get('versions')[0]; //TODO: mpacks - change this to the last item when sort order is fixed + //} + for (let i = 0, length = mpacks.length; i < length; i++) { + if (mpacks[i].get('name') === mpackName) { + return mpacks[i].get('versions')[0]; //TODO: mpacks - change this to the last item when sort order is fixed + } + } + } + + return null; + }, + + isSaved: function () { + const wizardController = this.get('wizardController'); + if (wizardController) { + return wizardController.getStepSavedState('selectMpacks'); + } + return false; + }.property('wizardController.content.stepsSavedState'), + + loadRegistryFailed: function () { + this.set('content.mpacks', []); + + App.showAlertPopup( + Em.I18n.t('common.error'), //header + Em.I18n.t('installer.selectMpacks.loadRegistryFailed') //body + ); + }, + + registryLoaded() { + const mpacks = this.get('content.mpacks'); + const mpackVersions = this.get('content.mpackVersions'); + const mpackServices = this.get('content.mpackServices'); + const mpackServiceVersions = this.get('content.mpackServiceVersions'); + const mpackUsecases = this.get('content.mpackUsecases'); + + if (!mpacks || mpacks.length === 0 + || !mpackVersions || mpackVersions.length === 0 + || !mpackServices || mpackServices.length === 0 + || !mpackServiceVersions || mpackServiceVersions.length === 0) { + return false; + } + + return true; + }, + + getRegistry: function () { + const deferred = $.Deferred(); + + if (!this.registryLoaded()) { + this.loadRegistry().then(data => { + this.loadRegistrySucceeded(data); + deferred.resolve(); + }, + () => { + this.loadRegistryFailed(); + deferred.reject(); + }); + } else { + deferred.resolve(); + } + + return deferred.promise(); + }, + + toggleMode: function () { + const isAdvancedMode = this.get('content.advancedMode'); + + if (isAdvancedMode) { //toggling to Basic Mode + this.clearSelection(); + } else { //toggling to Advanced Mode + this.set('noRecommendationAvailable', false); + } + + this.set('content.advancedMode', !isAdvancedMode); + }, + + loadStep: function () { + this.getRegistry().then(() => { + //add previously selected services + const selectedServices = this.get('content.selectedServices'); + if (selectedServices) { + selectedServices.forEach(service => { + this.addService(service.id); + }); + } + }); + }, + + isSubmitDisabled: function () { + const mpackServiceVersions = this.get('content.mpackServiceVersions'); + return App.get('router.btnClickInProgress') + || (this.get('wizardController.errors') && this.get('wizardController.errors').length > 0) + || mpackServiceVersions.filterProperty('selected', true).length === 0; + }.property('content.mpackServiceVersions.@each.selected', 'App.router.btnClickInProgress', 'wizardController.errors'), + + getMpackVersionById: function (versionId) { + const mpackVersions = this.get('content.mpackVersions'); + const byVersionId = version => version.id === versionId; + + if (mpackVersions) { + const version = mpackVersions.find(byVersionId); + return version; + } + + return null; + }, + + getServiceVersionById: function (versionId) { + const serviceVersions = this.get('content.mpackServiceVersions'); + const byVersionId = version => version.id === versionId; + + if (serviceVersions) { + const version = serviceVersions.find(byVersionId); + return version; + } + + return null; + }, + + getUsecaseById: function (usecaseId) { + const usecases = this.get('content.mpackUsecases'); + const byUsecaseId = usecase => usecase.id === usecaseId; + + if (usecases) { + const usecase = usecases.find(byUsecaseId); + return usecase; + } + + return null; + }, + + displayMpackVersion: function (versionId) { + const version = this.getMpackVersionById(versionId); + + if (version) { + version.mpack.versions.forEach(mpackVersion => { + if (mpackVersion.get('id') === versionId) { + mpackVersion.set('displayed', true); + } else { + mpackVersion.set('displayed', false); + } + }) + } + }, + + displayServiceVersion: function (versionId) { + const version = this.getServiceVersionById(versionId); + + if (version) { + version.service.versions.forEach(serviceVersion => { + if (serviceVersion.get('id') === versionId) { + serviceVersion.set('displayed', true); + } else { + serviceVersion.set('displayed', false); + } + }) + } + }, + + addMpackHandler: function (mpackVersionId) { + if (this.addMpack(mpackVersionId)) { + this.get('wizardController').setStepUnsaved('selectMpacks'); + } + }, + + addMpack: function (mpackVersionId) { + const mpackVersion = this.getMpackVersionById(mpackVersionId); + + if (mpackVersion) { + mpackVersion.services.forEach(service => this.addService(service.id)) + return true; + } + + return false; + }, + + toggleUsecaseHandler: function (usecaseId) { + if (this.toggleUsecase(usecaseId)) { + this.get('wizardController').setStepUnsaved('selectMpacks'); + } + }, + + toggleUsecase: function (usecaseId) { + this.clearSelection(); + + const usecase = this.getUsecaseById(usecaseId); + if (usecase) { + const selected = usecase.get('selected'); + usecase.set('selected', !selected); + + const usecasesSelected = this.get('selectedUseCases'); + if (usecasesSelected.length > 0) { + this.getUsecaseRecommendation() + .done(this.getUsecaseRecommendationSucceeded.bind(this)) + .fail(this.getUsecaseRecommendationFailed.bind(this)); + } + + return true; + } + + return false; + }, + + getUsecaseRecommendation: function (registryId) { + const usecases = this.get('content.mpackUsecases').filterProperty('selected').map(usecase => + ({ + scenario_name: usecase.name + }) + ); + + return App.ajax.send({ + name: 'registry.recommendation.usecases', + data: { + registryId: registryId || 1, + usecases: usecases + }, + showLoadingPopup: true, + sender: this + }); + }, + + getUsecaseRecommendationSucceeded: function (data) { + this.clearSelection(); + + let recommendations; + if (data && data.resources && data.resources.length > 0 && data.resources[0].recommendations) { + recommendations = data.resources[0].recommendations.mpack_bundles; + } + + if (recommendations && recommendations.length > 0 + && recommendations[0].mpacks && recommendations[0].mpacks.length > 0) { + const mpackVersionIds = recommendations[0].mpacks.map(mpack => mpack.mpack_name + mpack.mpack_version); + mpackVersionIds.forEach(this.addMpack.bind(this)); + } else { + this.set('noRecommendationAvailable', true); + } + }, + + getUsecaseRecommendationFailed: function () { + App.showAlertPopup( + Em.I18n.t('common.error'), //header + Em.I18n.t('installer.selectMpacks.getRecommendationFailed') //body + ); + }, + + addServiceHandler: function (serviceId) { + if (this.addService(serviceId)) { + this.get('wizardController').setStepUnsaved('selectMpacks'); + } + }, + + addService: function (serviceId) { + const service = this.getServiceVersionById(serviceId); + + if (service) { + service.set('selected', true); + service.set('mpackVersion.selected', true); + return true; + } + + return false; + }, + + removeServiceHandler: function (serviceId) { + if (this.removeService(serviceId)) { + this.get('wizardController').setStepUnsaved('selectMpacks'); + } + }, + + removeService: function (serviceId) { + const service = this.getServiceVersionById(serviceId); + + if (service) { + service.set('selected', false); + service.set('mpackVersion.selected', service.get('mpackVersion.services').some(s => s.get('selected') === true)); + return true; + } + + return false; + }, + + removeMpackHandler: function (mpackId) { + if (this.removeMpack(mpackId)) { + this.get('wizardController').setStepUnsaved('selectMpacks'); + } + }, + + removeMpack: function (mpackId) { + const mpackVersion = this.getMpackVersionById(mpackId); + + if (mpackVersion) { + mpackVersion.get('services').forEach(service => this.removeService(service.get('id'))); + return true; + } + + return false; + }, + + selectedUseCases: function () { + return this.get('content.mpackUsecases').filterProperty('selected'); + }.property('content.mpackUsecases.@each.selected'), + + selectedServices: function () { + const mpackServiceVersions = this.get('content.mpackServiceVersions'); + return mpackServiceVersions ? mpackServiceVersions.filter(s => s.get('selected') === true) : []; + }.property('content.mpackServiceVersions.@each.selected'), + + selectedMpackVersions: function () { + const versions = this.get('content.mpackVersions'); + return versions ? versions.filter(v => v.get('selected') === true) : []; + }.property('content.mpackVersions.@each.selected', 'selectedServices'), + + hasSelectedMpackVersions: function () { + const versions = this.get('content.mpackVersions'); + return versions ? versions.some(v => v.get('selected') === true) : false; + }.property('content.mpackVersions.@each.selected', 'selectedServices'), + + clearSelection: function () { + const mpackServiceVersions = this.get('content.mpackServiceVersions'); + if (mpackServiceVersions) { + mpackServiceVersions.setEach('selected', false); + } + + const versions = this.get('content.mpackVersions'); + if (versions) { + versions.setEach('selected', false); + } + + if (this.get('content.advancedMode')) { + const usecases = this.get('content.mpackUsecases'); + if (usecases) { + usecases.setEach('selected', false); + } + } + + this.set('noRecommendationAvailable', false); + this.get('wizardController').setStepUnsaved('selectMpacks'); + }, + + filteredMpacks: function () { + const mpacks = this.get('content.mpacks'); + const filterText = this.get('filterMpacksText').toLowerCase(); + + if (filterText.length > 2) { + const filteredMpacks = mpacks.filter(mpack => { + return mpack.get('filterOn').indexOf(filterText) > -1; + }); + + return filteredMpacks; + } + + return mpacks; + }.property('content.mpacks', 'filterMpacksText'), + + clearFilterMpacks: function () { + this.set('filterMpacksText', ""); + }, + + filteredServices: function () { + const services = this.get('content.mpackServices'); + const filterText = this.get('filterServicesText').toLowerCase(); + + if (filterText.length > 2) { + const filteredServices = services.filter(service => { + return service.get('filterOn').indexOf(filterText) > -1; + }); + + return filteredServices; + } + + return services; + }.property('content.mpackServices', 'filterServicesText'), + + clearFilterServices: function () { + this.set('filterServicesText', ""); + }, + + /** + * Onclick handler for Next button. + * Disable 'Next' button while it is already under process. (using Router's property 'nextBtnClickInProgress') + * @method submit + */ + submit: function () { + if (App.get('router.nextBtnClickInProgress')) { + return; + } + + if (!this.get('isSubmitDisabled')) { + const selectedServices = this.get('selectedServices').map(service => + ({ + id: service.id, + name: service.name, + mpackName: service.mpackVersion.mpack.name, + mpackVersion: service.mpackVersion.version + }) + ); + this.set('content.selectedServices', selectedServices); + + const selectedServiceNames = selectedServices.map(service => service.name); + this.set('content.selectedServiceNames', selectedServiceNames); + + const selectedMpacks = this.get('selectedMpackVersions').map(mpackVersion => { + const selectedMpack = { + id: `${mpackVersion.mpack.name}-${mpackVersion.version}`, + name: mpackVersion.mpack.name, + displayName: mpackVersion.mpack.displayName, + publicUrl: mpackVersion.mpackUrl, + downloadUrl: mpackVersion.mpackUrl, + version: mpackVersion.version + }; + + const oldSelectedMpacks = this.get('content.selectedMpacks'); + let oldSelectedMpack; + if (oldSelectedMpacks) { + oldSelectedMpack = oldSelectedMpacks.find(mpack => mpack.name === mpackVersion.mpack.name && mpack.version === mpackVersion.version); + } + if (oldSelectedMpack) { + selectedMpack.downloadUrl = oldSelectedMpack.downloadUrl; + selectedMpack.operatingSystems = oldSelectedMpack.operatingSystems; + } + + return selectedMpack; + }); + this.set('content.selectedMpacks', selectedMpacks); + + App.router.send('next'); + } + } +}); diff --git a/ambari-web/app/controllers/wizard/step0_controller.js b/ambari-web/app/controllers/wizard/step0_controller.js index 270c7a4b21a..618b2b6389e 100644 --- a/ambari-web/app/controllers/wizard/step0_controller.js +++ b/ambari-web/app/controllers/wizard/step0_controller.js @@ -17,11 +17,14 @@ */ var App = require('app'); +require('./wizardStep_controller'); -App.WizardStep0Controller = Em.Controller.extend({ +App.WizardStep0Controller = App.WizardStepController.extend({ name: 'wizardStep0Controller', + stepName: 'step0', + /** * Is step submitted * @type {bool} diff --git a/ambari-web/app/controllers/wizard/step10_controller.js b/ambari-web/app/controllers/wizard/step10_controller.js index 6840c1389b1..ba8d2576793 100644 --- a/ambari-web/app/controllers/wizard/step10_controller.js +++ b/ambari-web/app/controllers/wizard/step10_controller.js @@ -17,8 +17,13 @@ */ var App = require('app'); +require('./wizardStep_controller'); -App.WizardStep10Controller = Em.Controller.extend({ +App.WizardStep10Controller = App.WizardStepController.extend({ + + name: 'wizardStep10Controller', + + stepName: 'step10', /** * List of data about installed cluster components (hosts, services etc) diff --git a/ambari-web/app/controllers/wizard/step1_controller.js b/ambari-web/app/controllers/wizard/step1_controller.js index c137d96b5dd..7e01f3f17fe 100644 --- a/ambari-web/app/controllers/wizard/step1_controller.js +++ b/ambari-web/app/controllers/wizard/step1_controller.js @@ -18,6 +18,7 @@ var App = require('app'); var arrayUtils = require('utils/array_utils'); +require('./wizardStep_controller'); /** * @typedef {Em.Object} StackType @@ -35,10 +36,12 @@ var StackType = Em.Object.extend({ isSelected: Em.computed.someBy('stacks', 'isSelected', true) }); -App.WizardStep1Controller = Em.Controller.extend({ +App.WizardStep1Controller = App.WizardStepController.extend({ name: 'wizardStep1Controller', + stepName: 'step1', + /** * Skip repo-validation * diff --git a/ambari-web/app/controllers/wizard/step2_controller.js b/ambari-web/app/controllers/wizard/step2_controller.js index 05813e28b3a..7caa3c61e99 100644 --- a/ambari-web/app/controllers/wizard/step2_controller.js +++ b/ambari-web/app/controllers/wizard/step2_controller.js @@ -19,11 +19,22 @@ var App = require('app'); var validator = require('utils/validator'); var lazyloading = require('utils/lazy_loading'); +require('./wizardStep_controller'); -App.WizardStep2Controller = Em.Controller.extend({ +App.WizardStep2Controller = App.WizardStepController.extend({ name: 'wizardStep2Controller', + parsedHostsText: Em.I18n.t('installer.step2.parsedHostsPlaceholder').format(0), + + preRegisteredHostFound: Em.I18n.t('installer.step2.preRegistered.hostCount').format(0), + + manuallyInstalledHosts: [], + + noPreRegisteredHosts: true, + + stepName: 'step2', + /** * List of not installed hostnames * @type {string[]} @@ -52,6 +63,8 @@ App.WizardStep2Controller = Em.Controller.extend({ */ inputtedAgainHostNames: [], + filterText: null, + /** * Is Installer Controller used * @type {bool} @@ -115,16 +128,36 @@ App.WizardStep2Controller = Em.Controller.extend({ */ hostsError: null, + isSaved: function () { + const wizardController = this.get('wizardController'); + if (wizardController) { + return wizardController.getStepSavedState('step2'); + } + return false; + }.property('wizardController.content.stepsSavedState'), + useSSH: function () { return !App.get('isHadoopWindowsStack'); }.property('App.isHadoopWindowsStack'), + useSshRegistration: function () { + this.set('content.installOptions.manualInstall', false); + this.set('content.installOptions.useSsh', true); + }, + + useManualInstall: function () { + if (!this.get('content.installOptions.manualInstall')) { + this.getManuallyInstalledHosts(); + } + this.set('content.installOptions.manualInstall', true); + this.set('content.installOptions.useSsh', false); + }, /** * Error-message if sshKey is empty, null otherwise * @type {string|null} */ sshKeyError: function () { - if (this.get('hasSubmitted') && this.get('manualInstall') === false && this.get('useSSH') && Em.isBlank(this.get('sshKey'))) { + if (this.get('hasSubmitted') && this.get('manualInstall') === false && !this.get('manualInstall') && Em.isBlank(this.get('sshKey'))) { return Em.I18n.t('installer.step2.sshKey.error.required'); } return null; @@ -163,11 +196,18 @@ App.WizardStep2Controller = Em.Controller.extend({ return null; }.property('agentUser', 'hasSubmitted', 'manualInstall'), + preRegisteredHostsError: Em.computed.and('manualInstall' ,'noPreRegisteredHosts'), + /** * is Submit button disabled * @type {bool} */ - isSubmitDisabled: Em.computed.or('hostsError', 'sshKeyError', 'sshUserError', 'sshPortError', 'agentUserError', 'App.router.btnClickInProgress'), + isSubmitDisabled: Em.computed.or('hostsError', 'sshKeyError', 'sshUserError', 'sshPortError', 'agentUserError', 'App.router.btnClickInProgress', 'preRegisteredHostsError'), + + loadStep: function () { + //save initial hostNames value to check later if changes were made + this.set('initialHostNames', this.get('content.installOptions.hostNames')); + }, installedHostNames: function () { var installedHostsName = []; @@ -181,22 +221,88 @@ App.WizardStep2Controller = Em.Controller.extend({ return installedHostsName; }.property('content.hosts'), + /** + * get hosts that are installed manually + return + */ + getManuallyInstalledHosts: function () { + App.ajax.send({ + name: 'hosts.confirmed.install', + sender: this, + success: 'getManuallyInstalledHostsSuccessCallback' + }); + }, + + getManuallyInstalledHostsSuccessCallback(response) { + var installedHosts = [], + self = this; + if (response.items.length > 0) { + response.items.forEach(function (item, indx) { + installedHosts.push(Em.Object.create({ + 'controller': self, + 'hostName': item.Hosts.host_name, + 'cpuCount': item.Hosts.cpu_count, + 'memory': (parseFloat(item.Hosts.total_mem) / (1024 * 1024)).toFixed(2) + " GB", + 'freeSpace': (parseFloat(item.Hosts.disk_info[0].available) / (1024 * 1024)).toFixed(2) + " GB", + 'isVisible': true, + + filterRow: function() { + var filter = self.get('filterText'); + if(filter && !(item.Hosts.host_name.toUpperCase().indexOf(filter.toUpperCase()) > -1)) { + this.set('isVisible', false); + } else { + this.set('isVisible', true); + } + }.observes('controller.filterText') + })); + }); + this.set('preRegisteredHostFound', Em.I18n.t('installer.step2.preRegistered.hostCount').format(installedHosts.length)); + this.set('manuallyInstalledHosts', installedHosts); + this.set('noPreRegisteredHosts', false); + } else { + this.set('preRegisteredHostFound', Em.I18n.t('installer.step2.preRegistered.hostCount').format(0)); + this.set('noPreRegisteredHosts', true); + this.set('manuallyInstalledHosts', []); + } + }, + + deleteRegisteredHost: function (event) { + var hostName = event.context; + App.ajax.send({ + name: 'common.delete.registered.host', + sender: this, + data: { + hostName: event.context + }, + success: 'deleteRegisteredHostSuccessCallback' + }); + }, + + deleteRegisteredHostSuccessCallback: function (response) { + this.getManuallyInstalledHosts(); + }, + /** * Set not installed hosts to the hostNameArr * @method updateHostNameArr */ updateHostNameArr: function () { - this.set('hostNameArr', this.get('hostNames').trim().split(new RegExp("\\s+", "g"))); - this.parseHostNamesAsPatternExpression(); - this.get('inputtedAgainHostNames').clear(); - var tempArr = [], - hostNameArr = this.get('hostNameArr'); - for (var i = 0; i < hostNameArr.length; i++) { - if (!this.get('installedHostNames').contains(hostNameArr[i])) { - tempArr.push(hostNameArr[i]); - } - else { - this.get('inputtedAgainHostNames').push(hostNameArr[i]); + var tempArr = []; + if (this.get('manualInstall')) { + tempArr = this.get('manuallyInstalledHosts').mapProperty('hostName'); + } else { + this.set('hostNameArr', this.get('hostNames').trim().split(new RegExp("\\s+", "g"))); + this.parseHostNamesAsPatternExpression(); + this.get('inputtedAgainHostNames').clear(); + + var hostNameArr = this.get('hostNameArr'); + for (var i = 0; i < hostNameArr.length; i++) { + if (!this.get('installedHostNames').contains(hostNameArr[i])) { + tempArr.push(hostNameArr[i]); + } + else { + this.get('inputtedAgainHostNames').push(hostNameArr[i]); + } } } this.set('hostNameArr', tempArr); @@ -221,12 +327,22 @@ App.WizardStep2Controller = Em.Controller.extend({ return result; }, + parseHosts: function () { + var hostNames = this.get('hostNames').trim().split(new RegExp("\\s+", "g")); + if (hostNames[0] == "") { + this.set('parsedHostsText', Em.I18n.t('installer.step2.parsedHostsPlaceholder').format(0)); + } else { + var parsedHostNames = this.parseHostNamesAsPatternExpression(hostNames); + this.set('parsedHostsText', (Em.I18n.t('installer.step2.parsedHostsPlaceholder').format(parsedHostNames.length) + '\n' + parsedHostNames.join('\n'))); + } + }.observes('hostNames'), + /** * Set hostsError if host names don't pass validation * @method checkHostError */ checkHostError: function () { - if (Em.isEmpty(this.get('hostNames').trim())) { + if (!this.get('manualInstall') && Em.isEmpty(this.get('hostNames').trim())) { this.set('hostsError', Em.I18n.t('installer.step2.hostName.error.required')); } else { @@ -298,10 +414,6 @@ App.WizardStep2Controller = Em.Controller.extend({ return false; } - if (this.get('isPattern')) { - this.hostNamePatternPopup(this.get('hostNameArr')); - return false; - } if (this.get('inputtedAgainHostNames.length')) { this.installedHostsPopup(); } @@ -313,14 +425,16 @@ App.WizardStep2Controller = Em.Controller.extend({ /** * check is there a pattern expression in host name textarea - * push hosts that match pattern in hostNamesArr + * push hosts that match pattern in hostNameArr * @method parseHostNamesAsPatternExpression */ - parseHostNamesAsPatternExpression: function () { + parseHostNamesAsPatternExpression: function (hostNamesToBeParsed) { this.set('isPattern', false); var hostNames = []; - this.get('hostNameArr').forEach(function (a) { + var hostNameArr = hostNamesToBeParsed || this.get('hostNameArr'); + + hostNameArr.forEach(function (a) { var hn, allPatterns = a.match(/\[\d*\-\d*\]/g), patternsNumber = allPatterns ? allPatterns.length : 0; @@ -336,7 +450,11 @@ App.WizardStep2Controller = Em.Controller.extend({ } }, this); - this.set('hostNameArr', hostNames.uniq()); + if(hostNamesToBeParsed) { + return hostNames; + } else { + this.set('hostNameArr', hostNames.uniq()); + } }, /** @@ -390,12 +508,6 @@ App.WizardStep2Controller = Em.Controller.extend({ this.warningPopup(); return false; } - - if (this.get('manualInstall') === true) { - this.manualInstallPopup(); - return false; - } - this.saveHosts(); App.router.send('next'); return true; @@ -474,43 +586,6 @@ App.WizardStep2Controller = Em.Controller.extend({ }); }, - /** - * Show notify that installation is manual - * save hosts - * @return {App.ModalPopup} - * @method manualInstallPopup - */ - manualInstallPopup: function () { - var self = this; - return App.ModalPopup.show({ - header: Em.I18n.t('installer.step2.manualInstall.popup.header'), - onPrimary: function () { - this.hide(); - self.saveHosts(); - App.router.send('next'); - }, - bodyClass: Em.View.extend({ - templateName: require('templates/wizard/step2ManualInstallPopup') - }) - }); - }, - - /** - * Warn to manually install ambari-agent on each host - * @method manualInstallWarningPopup - */ - manualInstallWarningPopup: function () { - if (!this.get('content.installOptions.useSsh')) { - App.ModalPopup.show({ - header: Em.I18n.t('common.warning'), - body: Em.I18n.t('installer.step2.manualInstall.info'), - encodeBody: false, - secondary: null - }); - } - this.set('content.installOptions.manualInstall', !this.get('content.installOptions.useSsh')); - }.observes('content.installOptions.useSsh'), - /** * Load java.home value frin server * @method setAmbariJavaHome @@ -546,6 +621,10 @@ App.WizardStep2Controller = Em.Controller.extend({ * @method saveHosts */ saveHosts: function () { + if (this.get('content.installOptions.hostNames') !== this.get('initialHostNames')) { + this.get('wizardController').setStepUnsaved('step2'); + } + var hosts = this.get('content.hosts'); //add previously installed hosts @@ -555,6 +634,7 @@ App.WizardStep2Controller = Em.Controller.extend({ } } + //this.set('content.installOptions.hostNames', this.get('hostNameArr')); this.set('content.hosts', $.extend(hosts, this.getHostInfo())); this.setAmbariJavaHome(); }, diff --git a/ambari-web/app/controllers/wizard/step3_controller.js b/ambari-web/app/controllers/wizard/step3_controller.js index b5b3cb063be..a62bf0159ce 100644 --- a/ambari-web/app/controllers/wizard/step3_controller.js +++ b/ambari-web/app/controllers/wizard/step3_controller.js @@ -19,11 +19,14 @@ var App = require('app'); var lazyloading = require('utils/lazy_loading'); var numberUtils = require('utils/number_utils'); +require('./wizardStep_controller'); -App.WizardStep3Controller = Em.Controller.extend(App.ReloadPopupMixin, App.CheckHostMixin, { +App.WizardStep3Controller = App.WizardStepController.extend(App.ReloadPopupMixin, App.CheckHostMixin, { name: 'wizardStep3Controller', + stepName: 'step3', + hosts: [], content: [], @@ -75,6 +78,14 @@ App.WizardStep3Controller = Em.Controller.extend(App.ReloadPopupMixin, App.Check return (this.get('isRegistrationInProgress') || !this.get('isWarningsLoaded')) && !this.get('isBootstrapFailed') || App.get('router.btnClickInProgress'); }.property('isRegistrationInProgress', 'isWarningsLoaded', 'isBootstrapFailed'), + isSaved: function () { + const wizardController = this.get('wizardController'); + if (wizardController) { + return wizardController.getStepSavedState('step3'); + } + return false; + }.property('wizardController.content.stepsSavedState'), + /** * Controller is using in Add Host Wizard * @return {bool} @@ -305,6 +316,7 @@ App.WizardStep3Controller = Em.Controller.extend(App.ReloadPopupMixin, App.Check if (!self.hosts.length) { self.set('isSubmitDisabled', true); } + self.get('wizardController').setStepUnsaved('step3'); }, Em.I18n.t('installer.step3.hosts.remove.popup.body')); }, @@ -318,16 +330,6 @@ App.WizardStep3Controller = Em.Controller.extend(App.ReloadPopupMixin, App.Check this.removeHosts([hostInfo]); }, - /** - * Remove selected hosts (click-handler) - * @return App.ModalPopup - * @method removeSelectedHosts - */ - removeSelectedHosts: function () { - var selectedHosts = this.get('hosts').filterProperty('isChecked', true); - return this.removeHosts(selectedHosts); - }, - /** * Show popup with the list of hosts which are selected * @return App.ModalPopup @@ -850,7 +852,7 @@ App.WizardStep3Controller = Em.Controller.extend(App.ReloadPopupMixin, App.Check function () { self._submitProceed(); }, - Em.I18n.t('installer.step3.hostWarningsPopup.hostHasWarnings'), null, Em.I18n.t('installer.step3.hostWarningsPopup.hostHasWarnings.header'), null, 'warning'); + Em.I18n.t('installer.step3.hostWarningsPopup.hostHasWarnings'), null, Em.I18n.t('common.warning'), null, 'warning'); } this._submitProceed(); }, diff --git a/ambari-web/app/controllers/wizard/step4_controller.js b/ambari-web/app/controllers/wizard/step4_controller.js index 9defa1281ba..03e0b662c29 100644 --- a/ambari-web/app/controllers/wizard/step4_controller.js +++ b/ambari-web/app/controllers/wizard/step4_controller.js @@ -17,11 +17,14 @@ */ var App = require('app'); +require('./wizardStep_controller'); App.WizardStep4Controller = Em.ArrayController.extend({ name: 'wizardStep4Controller', + stepName: 'step4', + /** * List of Services * @type {Object[]} @@ -48,8 +51,10 @@ App.WizardStep4Controller = Em.ArrayController.extend({ * @type {bool} */ isSubmitDisabled: function () { - return this.filterProperty('isSelected', true).filterProperty('isInstalled', false).length === 0 || App.get('router.btnClickInProgress'); - }.property('@each.isSelected', 'App.router.btnClickInProgress'), + return App.get('router.btnClickInProgress') + || (this.get('wizardController.errors') && this.get('wizardController.errors').length > 0) + || this.filterProperty('isSelected', true).filterProperty('isInstalled', false).length === 0; + }.property('@each.isSelected', 'App.router.btnClickInProgress', 'wizardController.errors'), /** * List of validation errors. Look to #createError method for information diff --git a/ambari-web/app/controllers/wizard/step5_controller.js b/ambari-web/app/controllers/wizard/step5_controller.js index 873665267a9..089251ed5db 100644 --- a/ambari-web/app/controllers/wizard/step5_controller.js +++ b/ambari-web/app/controllers/wizard/step5_controller.js @@ -17,11 +17,22 @@ */ var App = require('app'); +require('./wizardStep_controller'); -App.WizardStep5Controller = Em.Controller.extend(App.BlueprintMixin, App.AssignMasterComponents, { +App.WizardStep5Controller = App.WizardStepController.extend(App.BlueprintMixin, App.AssignMasterComponents, { name: "wizardStep5Controller", + stepName: 'step5', + + isSaved: function () { + const wizardController = this.get('wizardController'); + if (wizardController) { + return wizardController.getStepSavedState(this.get('stepName')); + } + return false; + }.property('wizardController.content.stepsSavedState'), + _goNextStepIfValid: function () { App.set('router.nextBtnClickInProgress', false); if (!this.get('submitDisabled')) { diff --git a/ambari-web/app/controllers/wizard/step6_controller.js b/ambari-web/app/controllers/wizard/step6_controller.js index 5e7358d20c7..2b287f0cf7f 100644 --- a/ambari-web/app/controllers/wizard/step6_controller.js +++ b/ambari-web/app/controllers/wizard/step6_controller.js @@ -19,6 +19,7 @@ var App = require('app'); var blueprintUtils = require('utils/blueprint'); var validationUtils = require('utils/validator'); +require('./wizardStep_controller'); /** * By Step 6, we have the following information stored in App.db and set on this @@ -32,10 +33,12 @@ var validationUtils = require('utils/validator'); * slaveComponentHosts: App.db.slaveComponentHosts (slave-components-to-hosts mapping the user selected in Step 6) * */ -App.WizardStep6Controller = Em.Controller.extend(App.HostComponentValidationMixin, App.HostComponentRecommendationMixin, { +App.WizardStep6Controller = App.WizardStepController.extend(App.HostComponentValidationMixin, App.HostComponentRecommendationMixin, { name: 'wizardStep6Controller', + stepName: 'step6', + /** * List of hosts * @type {object[]} @@ -95,6 +98,18 @@ App.WizardStep6Controller = Em.Controller.extend(App.HostComponentValidationMixi */ isInstallerWizard: Em.computed.equal('content.controllerName', 'installerController'), + isSaved: function () { + const wizardController = this.get('wizardController'); + if (wizardController) { + return wizardController.getStepSavedState('step6'); + } + return false; + }.property('wizardController.content.stepsSavedState'), + + hostsChanged: function () { + this.get('wizardController').setStepUnsaved('step6'); + }, + isAllCheckboxesEmpty: function() { var hosts = this.get('hosts'); for (var i = 0; i < hosts.length; i++) { @@ -255,6 +270,7 @@ App.WizardStep6Controller = Em.Controller.extend(App.HostComponentValidationMixi }); }); this.checkCallback(component); + this.hostsChanged(); }, /** @@ -289,6 +305,7 @@ App.WizardStep6Controller = Em.Controller.extend(App.HostComponentValidationMixi */ loadStep: function () { this.clearStep(); + var parentController = App.router.get(this.get('content.controllerName')); if (parentController && parentController.get('content.componentsFromConfigs')) { parentController.clearConfigActionComponents(); @@ -644,9 +661,14 @@ App.WizardStep6Controller = Em.Controller.extend(App.HostComponentValidationMixi var bluePrintsForValidation = this.getValidationBlueprint(); this.set('content.recommendationsHostGroups', bluePrintsForValidation); + //TODO - mpacks: Hard coded to use first mpack. Update when we are installing multiple mpacks. + const selectedMpacks = this.get('content.selectedMpacks'); + return this.validateSelectedHostComponents({ services: services, hosts: hostNames, + stackName: selectedMpacks[0].name, + stackVersion: selectedMpacks[0].version, blueprint: bluePrintsForValidation }); }, diff --git a/ambari-web/app/controllers/wizard/step7_controller.js b/ambari-web/app/controllers/wizard/step7_controller.js index 3b7130531c5..de3d0bd7a22 100644 --- a/ambari-web/app/controllers/wizard/step7_controller.js +++ b/ambari-web/app/controllers/wizard/step7_controller.js @@ -17,6 +17,7 @@ */ var App = require('app'); +require('./wizardStep_controller'); /** * By Step 7, we have the following information stored in App.db and set on this @@ -42,10 +43,12 @@ var App = require('app'); * @property {?object[]} slaveComponentHosts */ -App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, App.EnhancedConfigsMixin, App.ToggleIsRequiredMixin, App.GroupsMappingMixin, App.AddSecurityConfigs, App.KDCCredentialsControllerMixin, { +App.WizardStep7Controller = App.WizardStepController.extend(App.ServerValidatorMixin, App.EnhancedConfigsMixin, App.ToggleIsRequiredMixin, App.GroupsMappingMixin, App.AddSecurityConfigs, App.KDCCredentialsControllerMixin, { name: 'wizardStep7Controller', + stepName: 'step7', + /** * Contains all field properties that are viewed in this step * @type {object[]} @@ -143,11 +146,13 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, App.E * @type {bool} */ isSubmitDisabled: function () { - if (!this.get('stepConfigs.length')) return true; - if (this.get('submitButtonClicked')) return true; - if (App.get('router.btnClickInProgress')) return true; + if (!this.get('stepConfigs.length') + || this.get('submitButtonClicked') + || App.get('router.btnClickInProgress') + || (this.get('wizardController.errors') && this.get('wizardController.errors').length > 0) + ) return true; return !this.get('stepConfigs').filterProperty('showConfig', true).everyProperty('errorCount', 0) || this.get("miscModalVisible"); - }.property('stepConfigs.@each.errorCount', 'miscModalVisible', 'submitButtonClicked', 'App.router.btnClickInProgress'), + }.property('stepConfigs.@each.errorCount', 'miscModalVisible', 'submitButtonClicked', 'App.router.btnClickInProgress', 'wizardController.errors'), /** * List of selected to install service names @@ -477,9 +482,9 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, App.E if (!this.get('isConfigsLoaded')) { return; } + console.time('wizard loadStep: '); this.clearStep(); - var self = this; App.config.setPreDefinedServiceConfigs(this.get('addMiscTabToPage')); @@ -548,7 +553,7 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, App.E if (isRangerServiceAbsent) { var isExternalRangerSetup; if (isInstallerWizard) { - isExternalRangerSetup = configs.filterProperty('fileName','cluster-env.xml').findProperty('name','enable_external_ranger'); + isExternalRangerSetup = configs.filterProperty('fileName','cluster-settings.xml').findProperty('name','enable_external_ranger'); if (Em.isNone(isExternalRangerSetup) || isExternalRangerSetup.value !== "true") { App.config.removeRangerConfigs(this.get('stepConfigs')); } @@ -845,11 +850,12 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, App.E var localDB = { hosts: this.get('wizardController.content.hosts'), masterComponentHosts: this.get('wizardController.content.masterComponentHosts'), - slaveComponentHosts: this.get('wizardController.content.slaveComponentHosts'), - selectedStack: {} + slaveComponentHosts: this.get('wizardController.content.slaveComponentHosts') }; + var selectedRepoVersion, repoVersion; + if (this.get('wizardController.name') === 'addServiceController') { repoVersion = App.RepositoryVersion.find().filter(function(i) { return i.get('stackVersionType') === App.get('currentStackName') && @@ -861,9 +867,7 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, App.E } else { selectedRepoVersion = Em.getWithDefault(App.Stack.find().findProperty('isSelected', true) || {}, 'repositoryVersion', false); } - if (selectedRepoVersion) { - localDB.selectedStack = selectedRepoVersion; - } + var configsByService = {}, dependencies = this.get('configDependencies'); configs.forEach(function (_config) { diff --git a/ambari-web/app/controllers/wizard/step8_controller.js b/ambari-web/app/controllers/wizard/step8_controller.js index 444981e86f0..2ef1597ad86 100644 --- a/ambari-web/app/controllers/wizard/step8_controller.js +++ b/ambari-web/app/controllers/wizard/step8_controller.js @@ -19,11 +19,14 @@ var App = require('app'); var stringUtils = require('utils/string_utils'); var fileUtils = require('utils/file_utils'); +require('./wizardStep_controller'); -App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wizardDeployProgressControllerMixin, App.ConfigOverridable, App.ConfigsSaverMixin, { +App.WizardStep8Controller = App.WizardStepController.extend(App.AddSecurityConfigs, App.wizardDeployProgressControllerMixin, App.ConfigOverridable, App.ConfigsSaverMixin, { name: 'wizardStep8Controller', + stepName: 'step8', + /** * @type {boolean} */ @@ -137,19 +140,29 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz */ configGroups: [], - /** - * List of selected but not installed services - * @type {Object[]} - */ selectedServices: function () { - return this.get('content.services').filterProperty('isSelected', true).filterProperty('isInstalled', false); - }.property('content.services.@each.isSelected','content.services.@each.isInstalled').cacheable(), + const services = App.StackService.find().map(service => service); + return services; + }.property(), - /** - * List of installed services - * @type {Object[]} - */ - installedServices: Em.computed.filterBy('content.services', 'isInstalled', true), + selectedMpacks: function() { + return this.get('content.selectedMpacks') || this.get('wizardController').getDBProperty('selectedMpacks'); + }.property(), + + downloadConfig: function() { + return this.get('content.downloadConfig') || this.get('wizardController').getDBProperty('downloadConfig'); + }.property(), + + getSelectedStack: function() { + const selectedStack = this.get('content.selectedStack'); + const stack = this.get('wizardController').getStack(selectedStack.name, selectedStack.version); + return stack; + }, + + installedServices: function() { + const services = App.StackService.find().filter(service => service.get('isInstalled') === true); + return services; + }.property(), /** * Current cluster name @@ -301,7 +314,8 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz } } else { // from install wizard - var selectedStack = App.Stack.find().findProperty('isSelected', true); + var downloadConfig = this.get('downloadConfig'); + var selectedStack = this.getSelectedStack(); var allRepos = []; if (selectedStack && selectedStack.get('operatingSystems')) { selectedStack.get('operatingSystems').forEach(function (os) { @@ -319,7 +333,7 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz }, this); } allRepos.set('display_name', Em.I18n.t("installer.step8.repoInfo.displayName")); - this.get('clusterInfo').set('useRedhatSatellite', selectedStack.get('useRedhatSatellite')); + this.get('clusterInfo').set('useRedhatSatellite', downloadConfig.useRedhatSatellite); this.get('clusterInfo').set('repoInfo', allRepos); } }, @@ -689,14 +703,16 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz // delete any existing clusters to start from a clean slate // before creating a new cluster in install wizard // TODO: modify for multi-cluster support - this.getExistingClusterNames().complete(function () { - var clusterNames = self.get('clusterNames'); - if (self.get('isInstaller') && !App.get('testMode') && clusterNames.length) { - self.deleteClusters(clusterNames); - } else { - self.getExistingVersions(); - } - }); + // this.getExistingClusterNames().complete(function () { + // var clusterNames = self.get('clusterNames'); + // if (self.get('isInstaller') && !App.get('testMode') && clusterNames.length) { + // self.deleteClusters(clusterNames); + // } else { + // self.getExistingVersions(); + // } + // }); + + this.startDeploy(); }, /** @@ -900,42 +916,13 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz } }, - /** - * To Start deploy process - * @method startDeploy - */ - startDeploy: function () { - if (!this.get('isInstaller')) { - this._startDeploy(); - } else { - var installerController = App.router.get('installerController'); - var versionData = installerController.getSelectedRepoVersionData(); - if (versionData) { - var self = this; - installerController.postVersionDefinitionFileStep8(versionData.isXMLdata, versionData.data).done(function (versionInfo) { - if (versionInfo.id && versionInfo.stackName && versionInfo.stackVersion) { - var selectedStack = App.Stack.find().findProperty('isSelected', true); - if (selectedStack) { - selectedStack.set('versionInfoId', versionInfo.id); - } - installerController.updateRepoOSInfo(versionInfo, selectedStack).done(function() { - self._startDeploy(); - }); - } - }); - } else { - this._startDeploy(); - } - } - }, - /** * Start deploy process * @method startDeploy */ - _startDeploy: function () { + startDeploy: function () { this.createCluster(); - this.createServiceGroup(); + this.createServiceGroups(); this.createSelectedServices(); if (!this.get('isAddHost')) { if (this.get('isAddService')) { @@ -948,7 +935,8 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz this.applyConfigurationsToCluster(this.generateDesiredConfigsJSON(this.get('configs'), fileNamesToUpdate)); } } - this.createConfigurations(); + this.configureCluster(); + this.createServiceConfigurations(); this.applyConfigurationsToCluster(this.get('serviceConfigTags')); } this.createComponents(); @@ -979,11 +967,11 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz */ createCluster: function () { if (!this.get('isInstaller')) return; - var stackVersion = this.get('content.installOptions.localRepo') ? App.currentStackVersion.replace(/(-\d+(\.\d)*)/ig, "Local$&") : App.currentStackVersion; + const selectedStack = this.getSelectedStack() this.addRequestToAjaxQueue({ name: 'wizard.step8.create_cluster', data: { - data: JSON.stringify({ "Clusters": {"version": stackVersion}}) + data: JSON.stringify({ "Clusters": {"version": selectedStack.get('stackNameVersion')}}) }, success: 'createClusterSuccess' }); @@ -994,18 +982,45 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz }, /** - * Creates the servcegroup + * Creates one service group per mpack. + * Skip if no mpacks were selected. * Queued request * @method createServiceGroup */ - createServiceGroup: function () { + createServiceGroups: function () { if (!this.get('isInstaller')) return; - this.addRequestToAjaxQueue({ - name: 'wizard.step8.create_service_group', - data: { - data: JSON.stringify({ "ServiceGroupInfo": { "cluster_name": App.get('clusterName') || App.clusterStatus.get('clusterName'), "service_group_name": App.get('defaultServiceGroupName') }}) - } - }); + + var data = this.createServiceGroupsData(); + if (data) { + this.addRequestToAjaxQueue({ + name: 'wizard.step8.create_service_group', + data: { + data: JSON.stringify(data) + } + }); + } + }, + + /** + * Format data for createServiceGroups request + * @returns {Object[]} + * @method createServiceGroupsData + */ + createServiceGroupsData: function () { + const mpacks = this.get('selectedMpacks'); + + if (mpacks) { + const serviceGroups = mpacks.map(mpack => ({ + "ServiceGroupInfo": { + "service_group_name": `${mpack.name}-${mpack.version}`, + } + }) + ); + + return serviceGroups; + } + + return null; }, /** @@ -1031,13 +1046,19 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz * @method createSelectedServicesData */ createSelectedServicesData: function () { - var selectedStack; - if (this.get('isInstaller')) { - selectedStack = App.Stack.find().findProperty('isSelected', true); - } - return this.get('selectedServices').map(service => selectedStack ? - {"ServiceInfo": { "service_name": service.get('serviceName'), "service_type": service.get('serviceName'), "service_group_name": App.get('defaultServiceGroupName'), "desired_repository_version_id": selectedStack.get('versionInfoId') }} : - {"ServiceInfo": { "service_name": service.get('serviceName'), "service_type": service.get('serviceName'), "service_group_name": App.get('defaultServiceGroupName'), }}); + const services = this.get('selectedServices'); + const data = services.map(service => ({ + "ServiceInfo": { + "service_name": service.get('serviceName'), + "service_type": service.get('serviceName'), + //TODO: mpacks - needs to be revisited when we are no longer hard coding service groups to be named + // for mpacks and when the concept of a "selected stack" is no longer a thing + "service_group_name": `${service.get('stackName')}-${service.get('stackVersion')}`, + "desired_stack": `${service.get('stackName')}-${service.get('stackVersion')}`, + } + }) + ); + return data; }, /** @@ -1475,15 +1496,34 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz }, /** - * Create config objects for cluster and services - * @method createConfigurations + * Configure cluster settings */ - createConfigurations: function () { - if (this.get('isInstaller')) { - /** add cluster-env **/ - this.get('serviceConfigTags').pushObject(this.createDesiredConfig('cluster-env', this.get('configs').filterProperty('filename', 'cluster-env.xml'))); - } + configureCluster: function () { + const clusterSettings = this.get('configs').filterProperty('filename', 'cluster-settings.xml'); + const data = []; + clusterSettings.forEach(setting => { + data.push({ + "ClusterSettingInfo": { + "cluster_setting_name": setting.name, + "cluster_setting_value": setting.value + } + }) + }); + + this.addRequestToAjaxQueue({ + name: 'common.cluster.settings', + data: { + clusterName: this.get('clusterName'), + data: data + } + }); + }, + /** + * Create config objects for services + * @method createServiceConfigurations + */ + createServiceConfigurations: function () { this.get('selectedServices').forEach(function (service) { Object.keys(service.get('configTypes')).forEach(function (type) { if (!this.get('serviceConfigTags').someProperty('type', type)) { @@ -1543,14 +1583,6 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz })); } }, this); - var clusterConfig = serviceConfigTags.findProperty('type', 'cluster-env'); - if (clusterConfig) { - allConfigData.pushObject(JSON.stringify({ - Clusters: { - desired_config: [clusterConfig] - } - })); - } this.addRequestToAjaxQueue({ name: 'common.across.services.configurations', @@ -1886,7 +1918,7 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz //service configurations var totalConf = []; //Add cluster-env - var clusterEnv = this.get('configs').filterProperty('filename', 'cluster-env.xml'); + var clusterEnv = this.get('configs').filterProperty('filename', 'cluster-settings.xml'); var configurations = {}; configurations["cluster-env"] = self.getConfigurationDetailsForConfigType(clusterEnv); totalConf.push(configurations); @@ -2021,7 +2053,7 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz }, this); }, this); - var selectedStack = App.Stack.find().findProperty('isSelected', true); + var selectedStack = this.getSelectedStack(); blueprint = { 'configurations': totalConf, 'host_groups': host_groups.filter(function (item) { return item.cardinality > 0; }), diff --git a/ambari-web/app/controllers/wizard/step9_controller.js b/ambari-web/app/controllers/wizard/step9_controller.js index 9f27f654a6d..cd4dd304b39 100644 --- a/ambari-web/app/controllers/wizard/step9_controller.js +++ b/ambari-web/app/controllers/wizard/step9_controller.js @@ -17,11 +17,14 @@ */ var App = require('app'); var stringUtils = require('utils/string_utils'); +require('./wizardStep_controller'); -App.WizardStep9Controller = Em.Controller.extend(App.ReloadPopupMixin, { +App.WizardStep9Controller = App.WizardStepController.extend(App.ReloadPopupMixin, { name: 'wizardStep9Controller', + stepName: 'step9', + /** * Array of host Objects that are successfully registered on "Confirm Host Options" page * @@ -106,15 +109,17 @@ App.WizardStep9Controller = Em.Controller.extend(App.ReloadPopupMixin, { * @type {bool} */ isSubmitDisabled: function () { - var validStates = ['STARTED', 'START FAILED', 'START_SKIPPED']; + var validStates = ['INSTALLED', 'STARTED', 'START FAILED', 'START_SKIPPED']; var controllerName = this.get('content.controllerName'); if (controllerName == 'addHostController' || controllerName == 'addServiceController') { validStates.push('INSTALL FAILED'); } - return !validStates.contains(this.get('content.cluster.status')) || App.get('router.btnClickInProgress'); + return App.get('router.btnClickInProgress') + || (this.get('wizardController.errors') && this.get('wizardController.errors').length > 0) + || !validStates.contains(this.get('content.cluster.status')); }.property('content.cluster.status'), - isNextButtonDisabled: Em.computed.or('App.router.nextBtnClickInProgress', 'isSubmitDisabled'), + isNextButtonDisabled: Em.computed.or('App.router.nextBtnClickInProgress', 'isSubmitDisabled', 'wizardController.errors.length'), /** * Observer function: Enables previous steps link if install task failed in installer wizard. @@ -136,7 +141,17 @@ App.WizardStep9Controller = Em.Controller.extend(App.ReloadPopupMixin, { * Computed property to determine if the Retry button should be made visible on the page. * @type {bool} */ - showRetry: Em.computed.equal('content.cluster.status', 'INSTALL FAILED'), + showRetry: function () { + const status = this.get('content.cluster.status'); + switch (status) { + case 'INSTALL FAILED': + case 'INSTALLED': + case 'START FAILED': + return true; + } + + return false; + }.property('content.cluster.status'), /** * Observer function: Calls {hostStatusUpdates} function once with change in a host status from any registered hosts. diff --git a/ambari-web/app/controllers/wizard/verifyProducts_controller.js b/ambari-web/app/controllers/wizard/verifyProducts_controller.js new file mode 100644 index 00000000000..8a60310e0b2 --- /dev/null +++ b/ambari-web/app/controllers/wizard/verifyProducts_controller.js @@ -0,0 +1,162 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +var App = require('app'); +require('./wizardStep_controller'); + +App.WizardVerifyProductsController = App.WizardStepController.extend({ + + VERIFYREPO_INPROGRESS: 0, + VERIFYREPO_SUCCEEDED: 1, + VERIFYREPO_FAILED: 2, + + name: 'wizardVerifyProductsController', + + stepName: 'verifyProducts', + + mpacks: [], + + repos: [], + + loadStep: function () { + const selectedMpacks = this.get('content.selectedMpacks'); + + const mpacks = []; + selectedMpacks.forEach(mpack => { + mpacks.pushObject(Em.Object.create({ + name: mpack.name, + displayName: mpack.displayName, + publicUrl: mpack.publicUrl, + downloadUrl: mpack.downloadUrl, + version: mpack.version, + operatingSystems: mpack.operatingSystems.map(os => + Em.Object.create({ + type: os.type, + selected: os.selected, + isFirstSelected: os.isFirstSelected, + isLastSelected: os.isLastSelected, + repos: os.repos.map(repo => + Em.Object.create({ + id: repo.id, //this one is globally unique + repoId: repo.repoId, //this is the one displayed in the UI + downloadUrl: repo.downloadUrl, + isFirst: repo.isFirst, + isLast: repo.isLast, + inProgress: true, + succeeded: false, + failed: false + }) + ) + }) + ) + })); + }); + this.set('mpacks', mpacks); + + const repos = this.get('mpacks').reduce( + (repos, mpack) => repos.concat( + mpack.get('operatingSystems').reduce( + (repos, os) => repos.concat( + os.get('repos') + ), + [] + ) + ), + [] + ); + this.set('repos', repos); + + repos.forEach(repo => this.verifyRepo(repo).then(this.verifyRepoSucceeded.bind(this), this.verifyRepoFailed.bind(this))); + }, + + /** + * Ensures that repo state flags remain in sync. + * + * @param {any} repo to change state of + * @param {any} state to change to + */ + setRepoState: function (repo, state) { + switch (state) { + case this.get('VERIFYREPO_INPROGRESS'): + repo.set('succeeded', false); + repo.set('failed', false); + repo.set('inProgress', true); + break; + case this.get('VERIFYREPO_SUCCEEDED'): + repo.set('succeeded', true); + repo.set('failed', false); + repo.set('inProgress', false); + break; + case this.get('VERIFYREPO_FAILED'): + repo.set('succeeded', false); + repo.set('failed', true); + repo.set('inProgress', false); + break; + } + }, + + /** + * This will be a no-op until the server actually has some way of verifying repos. + * + * @param {any} repo + */ + verifyRepo: function (repo) { + const dfd = $.Deferred(); + + const self = this; + setTimeout(function () { + self.setRepoState(repo, self.get('VERIFYREPO_INPROGRESS')); + dfd.resolve(repo); + }); + + return dfd.promise(); + }, + + verifyRepoSucceeded: function (repo) { + this.setRepoState(repo, this.get('VERIFYREPO_SUCCEEDED')); + }, + + verifyRepoFailed: function (repo) { + this.setRepoState(repo, this.get('VERIFYREPO_FAILED')); + }, + + retryVerifyRepo: function (repo) { + this.verifyRepo(repo).then(this.verifyRepoSucceeded.bind(this), this.verifyRepoFailed.bind(this)); + }, + + isStepDisabled: function (stepIndex, currentIndex) { + const normallyDisabled = this._super(stepIndex, currentIndex); + const useCustomRepo = this.get('wizardController.content.downloadConfig.useCustomRepo'); + + return normallyDisabled || !useCustomRepo; + }, + + isSubmitDisabled: function () { + const repos = this.get('repos'); + return App.get('router.btnClickInProgress') + || (this.get('wizardController.errors') && this.get('wizardController.errors').length > 0) + || repos.filterProperty('succeeded', false).length > 0; + }.property('repos.@each.succeeded', 'App.router.btnClickInProgress', 'wizardController.errors'), + + submit: function () { + if (App.get('router.nextBtnClickInProgress')) { + return; + } + + App.router.send('next'); + } +}); diff --git a/ambari-web/app/controllers/wizard/wizardStep_controller.js b/ambari-web/app/controllers/wizard/wizardStep_controller.js new file mode 100644 index 00000000000..a8c2803539a --- /dev/null +++ b/ambari-web/app/controllers/wizard/wizardStep_controller.js @@ -0,0 +1,42 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +/* + A base class for wizard step controllers. +*/ +App.WizardStepController = Em.Controller.extend({ + /** + * Determines whether the step should be disabled. + * This is a base implementation that should be extended + * in derived classes to provide special case logic. + * The base implementation returns true if the step being checked + * is after the current step. + * + * @returns true if the step should be disabled + */ + isStepDisabled: function () { + const wizardController = this.get('wizardController'); + const currentIndex = wizardController.get('currentStep'); + const stepName = this.get('stepName'); + const stepIndex = wizardController.getStepIndex(stepName); + + return stepIndex > currentIndex; + } +}); \ No newline at end of file diff --git a/ambari-web/app/data/configs/wizards/kerberos_identities.js b/ambari-web/app/data/configs/wizards/kerberos_identities.js index 8af6702d1d9..e5fd2d4567a 100644 --- a/ambari-web/app/data/configs/wizards/kerberos_identities.js +++ b/ambari-web/app/data/configs/wizards/kerberos_identities.js @@ -24,21 +24,21 @@ module.exports = { "name": "smokeuser_principal_name", "displayName": "Smoke user principal", "category": "Ambari Principals", - "filename": "cluster-env.xml", + "filename": "cluster-settings.xml", "index": 1 }, { "name": "smokeuser_keytab", "displayName": "Smoke user keytab", "category": "Ambari Principals", - "filename": "cluster-env.xml", + "filename": "cluster-settings.xml", "index": 2 }, { "name": "ambari-server_keytab", "displayName": "Ambari Keytab", "category": "Ambari Principals", - "filename": "cluster-env.xml", + "filename": "cluster-settings.xml", "index": 3 }, { diff --git a/ambari-web/app/mappers.js b/ambari-web/app/mappers.js index cde9bcc62f4..38d9c0829fb 100644 --- a/ambari-web/app/mappers.js +++ b/ambari-web/app/mappers.js @@ -19,6 +19,7 @@ //load all mappers require('mappers/server_data_mapper'); require('mappers/stack_service_mapper'); +require('mappers/mpack_service_mapper'); require('mappers/stack_mapper'); require('mappers/stack_version_mapper'); require('mappers/configs/themes_mapper'); @@ -44,4 +45,4 @@ require('mappers/alert_notification_mapper'); require('mappers/root_service_mapper'); require('mappers/widget_mapper'); require('mappers/widget_layout_mapper'); -require('mappers/stack_upgrade_history_mapper'); \ No newline at end of file +require('mappers/stack_upgrade_history_mapper'); diff --git a/ambari-web/app/mappers/configs/stack_config_properties_mapper.js b/ambari-web/app/mappers/configs/stack_config_properties_mapper.js index 75a55643c26..e9f2a4baf0d 100644 --- a/ambari-web/app/mappers/configs/stack_config_properties_mapper.js +++ b/ambari-web/app/mappers/configs/stack_config_properties_mapper.js @@ -64,29 +64,43 @@ App.stackConfigPropertiesMapper = App.QuickDataMapper.create({ map: function (json) { console.time('App.stackConfigPropertiesMapper execution time'); - if (json && json.Versions) { - //hack for cluster versions - json = {items: [json]}; - var clusterConfigs = true; - } if (json && json.items) { + //check if we are working with cluster settings + let clusterConfigs = false; + if (json.items[0].ClusterSettingsInfo) { + //change layout to match service configs so we can use same code to process + json = { + items: [ + { configurations: json.items } + ] + } + + clusterConfigs = true; + } + var configs = []; - json.items.forEach(function(stackItem) { - var configTypeInfo = clusterConfigs ? Em.get(stackItem, 'Versions.config_types') : Em.get(stackItem, 'StackServices.config_types'); + json.items.forEach(function (stackItem) { + if (!clusterConfigs) { + var configTypeInfo = Em.get(stackItem, 'StackServices.config_types'); + } - stackItem.configurations.forEach(function(config) { + stackItem.configurations.forEach(function (config) { if (clusterConfigs) { - config.StackConfigurations = config.StackLevelConfigurations; + config.StackConfigurations = config.ClusterSettingsInfo; } var configType = App.config.getConfigTagFromFileName(config.StackConfigurations.type); config.id = App.config.configId(config.StackConfigurations.property_name, configType); config.recommended_is_final = config.StackConfigurations.final === "true"; - config.supports_final = !!configTypeInfo[configType] && configTypeInfo[configType].supports.final === "true"; + if (!clusterConfigs) { + config.supports_final = !!configTypeInfo[configType] && configTypeInfo[configType].supports.final === "true"; + } else { + config.supports_final = false; + } var attributes = config.StackConfigurations.property_value_attributes; if (attributes) { config.is_required = this._isRequired(attributes.empty_value_valid, config.StackConfigurations.property_value); - config.is_reconfigurable = !(attributes.editable_only_at_install || config.StackConfigurations.type === 'cluster-env.xml'); + config.is_reconfigurable = !(attributes.editable_only_at_install || config.StackConfigurations.type === 'cluster-settings.xml'); config.is_editable = !attributes.read_only; config.is_required_by_agent = !attributes.ui_only_property; } diff --git a/ambari-web/app/mappers/mpack_service_mapper.js b/ambari-web/app/mappers/mpack_service_mapper.js new file mode 100644 index 00000000000..72cffa3447a --- /dev/null +++ b/ambari-web/app/mappers/mpack_service_mapper.js @@ -0,0 +1,114 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var App = require('app'); + +App.MpackServiceMapper = App.QuickDataMapper.create({ + model: App.StackService, + component_model: App.StackServiceComponent, + + config: { + id: 'service_name', + stack_id: 'stack_id', + service_name: 'service_name', + service_type: 'service_type', + display_name: 'display_name', + config_types: 'config_types', + comments: 'comments', + service_version: 'service_version', + stack_name: 'stack_name', + stack_version: 'stack_version', + selection: 'selection', + is_mandatory: 'is_mandatory', + is_selected: 'is_selected', + is_installed: 'is_installed', + is_installable: 'is_installable', + is_service_with_widgets: 'is_service_with_widgets', + required_services: 'required_services', + service_check_supported: 'service_check_supported', + support_delete_via_ui: 'support_delete_via_ui', + service_components_key: 'service_components', + service_components_type: 'array', + service_components: { + item: 'id' + } + }, + + component_config: { + id: 'component_name', + component_name: 'component_name', + display_name: 'display_name', + cardinality: 'cardinality', + custom_commands: 'custom_commands', + reassign_allowed : 'reassign_allowed', + decommission_allowed: 'decommission_allowed', + has_bulk_commands_definition: 'has_bulk_commands_definition', + bulk_commands_display_name: 'bulk_commands_display_name', + bulk_commands_master_component_name: 'bulk_commands_master_component_name', + service_name: 'service_name', + component_category: 'component_category', + rolling_restart_supported: 'rolling_restart_supported', + is_master: 'is_master', + is_client: 'is_client', + stack_name: 'stack_name', + stack_version: 'stack_version', + stack_service_id: 'service_name', + dependencies_key: 'dependencies', + dependencies_type: 'array', + dependencies: { + item: 'Dependencies' + } + }, + + map: function (service) { + var model = this.get('model'); + var result = []; + var stackServiceComponents = []; + var nonInstallableServices = ['KERBEROS']; + var displayOrderLength = App.StackService.displayOrder.length; + var stackService = service.StackServices; + var serviceComponents = []; + service.components.forEach(function (serviceComponent) { + var dependencies = serviceComponent.dependencies.map(function (dependecy) { + return { Dependencies: App.keysUnderscoreToCamelCase(App.permit(dependecy.Dependencies, ['component_name', 'scope', 'service_name'])) }; + }); + serviceComponent.StackServiceComponents.id = serviceComponent.StackServiceComponents.component_name; + serviceComponent.StackServiceComponents.dependencies = dependencies; + serviceComponents.push(serviceComponent.StackServiceComponents); + var parsedResult = this.parseIt(serviceComponent.StackServiceComponents, this.get('component_config')); + if (parsedResult.id == 'MYSQL_SERVER') { + parsedResult.custom_commands = parsedResult.custom_commands.without('CLEAN'); + } + stackServiceComponents.push(parsedResult); + }, this); + stackService.stack_id = stackService.stack_name + '-' + stackService.stack_version; + stackService.service_components = serviceComponents; + stackService.is_service_with_widgets = service.artifacts.someProperty('Artifacts.artifact_name', 'widgets_descriptor'); + // @todo: replace with server response value after API implementation + if (nonInstallableServices.contains(stackService.service_name)) { + stackService.is_installable = false; + stackService.is_selected = false; + } + if(stackService.selection === "MANDATORY") { + stackService.is_mandatory = true; + } + result.push(this.parseIt(stackService, this.get('config'))); + + App.store.safeLoadMany(this.get('component_model'), stackServiceComponents); + App.store.safeLoadMany(model, result); + } +}); diff --git a/ambari-web/app/mappers/stack_mapper.js b/ambari-web/app/mappers/stack_mapper.js index 323a033ac3b..18eaf1dc586 100644 --- a/ambari-web/app/mappers/stack_mapper.js +++ b/ambari-web/app/mappers/stack_mapper.js @@ -22,7 +22,7 @@ App.stackMapper = App.QuickDataMapper.create({ modelOS: App.OperatingSystem, modelRepo: App.Repository, modelServices: App.ServiceSimple, - + configStack: { id: 'id', stack_name: 'stack_name', @@ -51,7 +51,7 @@ App.stackMapper = App.QuickDataMapper.create({ item: 'id' } }, - + configOS: { id: 'id', os_type: 'os_type', @@ -72,11 +72,11 @@ App.stackMapper = App.QuickDataMapper.create({ display_name: 'display_name', latest_version: 'latest_version' }, - + configRepository: { id: 'id', base_url: 'base_url', - base_url_init: 'base_url', + base_url_init: 'public_url', default_base_url: 'default_base_url', latest_base_url: 'latest_base_url', mirrors_list: 'mirrors_list', @@ -88,9 +88,10 @@ App.stackMapper = App.QuickDataMapper.create({ operating_system_id: 'os_id', components: 'components', distribution: 'distribution', + unique: 'unique', tags: 'tags' }, - + map: function(json) { var modelStack = this.get('modelStack'); var modelOS = this.get('modelOS'); @@ -105,6 +106,7 @@ App.stackMapper = App.QuickDataMapper.create({ if (!stack.id) { stack.id = stack.stack_name + '-' + stack.stack_version + '-' + stack.repository_version; //HDP-2.5-2.5.0.0 } + var operatingSystemsArray = []; var servicesArray = []; @@ -126,7 +128,7 @@ App.stackMapper = App.QuickDataMapper.create({ operatingSystems.is_selected = ops.isSelected == true || ops.isSelected == undefined; resultOS.push(this.parseIt(operatingSystems, this.get('configOS'))); operatingSystemsArray.pushObject(operatingSystems); - + }, this); stack.stack_services.forEach(function(service) { diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js index b940874daef..1488fc4fa7e 100644 --- a/ambari-web/app/messages.js +++ b/ambari-web/app/messages.js @@ -62,308 +62,319 @@ Em.I18n.translations = { 'app.aboutAmbari.version': 'Version', 'app.aboutAmbari.licensed': 'Licensed under the Apache License, Version 2.0', - 'apply':'apply', - 'and':'and', - 'none':'none', + 'add': 'Add', 'all':'all', - 'minimum':'minimum', - 'from':'From', - 'to':'To', - 'ok':'OK', + 'and':'and', + 'any': 'Any', + 'apply':'apply', 'as':'as', - 'on':'on', + 'from':'From', 'in':'in', - 'any': 'Any', + 'it': 'it', + 'minimum':'minimum', 'more':'more', - 'yes':'Yes', 'no':'No', - 'add': 'Add', + 'none':'none', + 'ok':'OK', + 'on':'on', 'op': 'op', 'ops': 'ops', 'or': 'or', 'then': 'then', - 'it': 'it', - + 'to':'To', + 'yes':'Yes', + 'common.abort': 'Abort', + 'common.aborted': 'Aborted', + 'common.aboutAmbari': 'About', 'common.access':'Access', - 'common.learnMore':'Learn more', - 'common.showDetails':'Show Details', - 'common.back':'Back', - 'common.prev':'Prev', - 'common.next':'Next', - 'common.host':'Host', - 'common.hosts':'Hosts', - 'common.services':'Services', - 'common.group':'Group', - 'common.groups':'Groups', - 'common.progress':'Progress', - 'common.status':'Status', 'common.action':'Action', - 'common.refresh':'Refresh', - 'common.remove':'Remove', - 'common.retry':'Retry', - 'common.skip':'Skip', - 'common.filter': 'Filter', - 'common.rollBack':'Rollback', - 'common.show':'Show', - 'common.hide':'Hide', - 'common.cancel':'Cancel', + 'common.actions': 'Actions', + 'common.activity': 'Activity', + 'common.add': 'Add', + 'common.additional': 'Additional', + 'common.advanced': 'Advanced', + 'common.alertDefinition': "Alert Definition", + 'common.all.clients':'All Clients', + 'common.all':'All', + 'common.allServices':'All Services', 'common.apply':'Apply', - 'common.done':'Done', - 'common.failed':'Failed', - 'common.service': 'Service', - 'common.version':'Version', - 'common.downgrade':'Downgrade', - 'common.description':'Description', - 'common.default':'Default', + 'common.author': 'Author', + 'common.back':'Back', + 'common.backgroundOperations': 'Background Operations', + 'common.cancel':'Cancel', + 'common.change': 'Change', 'common.client':'Client', - 'common.master':'Master', - 'common.slave':'Slave', - 'common.zookeeper':'ZooKeeper', - 'common.hbase':'HBase', - 'common.regionServer':'RegionServer', - 'common.taskTracker':'TaskTracker', - 'common.dataNode':'DataNode', - 'common.more': 'More...', - 'common.print':'Print', - 'common.deploy':'Deploy', - 'common.generate.blueprint':'Generate Blueprint', - 'common.message':'Message', - 'common.tasks':'Tasks', - 'common.taskLog':'Task Log', - 'common.open':'Open', - 'common.copy':'Copy', + 'common.clients':'Clients', + 'common.clone': 'Clone', + 'common.close': 'Close', + 'common.cluster':'Cluster', + 'common.compare': 'Compare', 'common.complete':'Complete', 'common.completed':'Completed', - 'common.metrics':'Metrics', - 'common.timeRange':'Time Range', - 'common.name':'Name', - 'common.key':'Key', - 'common.value':'Value', - 'common.ipAddress':'IP Address', - 'common.rack':'Rack', - 'common.cpu':'CPU', - 'common.cores': 'Cores', + 'common.component':'Component', + 'common.components':'Components', + 'common.conf.group': 'Configuration Group', + 'common.configGroup': 'Config Group', + 'common.configs': "Configs", + 'common.configuration': "Configuration", + 'common.confirm': 'Confirm', + 'common.continueAnyway': 'Continue Anyway', + 'common.copy':'Copy', 'common.cores.cpu': 'Cores (CPU)', - 'common.ram':'RAM', - 'common.disabled':'Disabled', - 'common.enabled':'Enabled', - 'common.enableAll':'Enable All', + 'common.cores': 'Cores', + 'common.cpu':'CPU', + 'common.critical': 'Critical', + 'common.csv': 'Save as CSV', + 'common.current': 'Current', + 'common.custom': 'Custom', + 'common.dataNode':'DataNode', + 'common.dataSet': 'Data Set', + 'common.date': 'Date', + 'common.days': "Days", + 'common.decommission':'Decommission', + 'common.default':'Default', + 'common.delete': 'Delete', + 'common.deploy':'Deploy', + 'common.description':'Description', + 'common.details':'Details', + 'common.direction': 'Direction', + 'common.disable': 'Disable', 'common.disableAll':'Disable All', + 'common.disabled':'Disabled', + 'common.discard': 'Discard', 'common.disk':'Disk', 'common.diskUsage':'Disk Usage', - 'common.last':'Last', - 'common.loadAvg':'Load Avg', - 'common.components':'Components', - 'common.component':'Component', - 'common.quickLinks':'Quick Links', - 'common.save':'Save', - 'common.saveAnyway':'Save Anyway', - 'common.servers':'Servers', - 'common.clients':'Clients', - 'common.all.clients':'All Clients', - 'common.user': 'User', - 'common.users': 'Users', - 'common.issues': 'Issues', - 'common.os':'OS', - 'common.oss':'OSs', - 'common.memory':'Memory', - 'common.maximum':'Maximum', - 'common.started':'Started', - 'common.start':'Start', - 'common.stop':'Stop', - 'common.pause':'Pause', + 'common.dismiss': "Dismiss", + 'common.documentation': "Documentation", + 'common.done':'Done', + 'common.downgrade':'Downgrade', + 'common.download': 'Download', + 'common.duplicate': 'Duplicate', + 'common.duration': 'Duration', + 'common.edit': 'Edit', + 'common.empty': 'Empty', + 'common.enable': 'Enable', + 'common.enableAll':'Enable All', + 'common.enabled':'Enabled', + 'common.end.time': 'End Time', 'common.end':'End', - 'common.decommission':'Decommission', - 'common.recommission':'Recommission', + 'common.enter': 'Enter', + 'common.error': 'Error', + 'common.errorPopup.header': 'An error has been encountered', + 'common.exclude.short': 'Excl', + 'common.exclude': 'Exclude', + 'common.exitAnyway': 'Exit Anyway', + 'common.export': 'Export', + 'common.express.downgrade': 'Express Downgrade', + 'common.express': 'Express', + 'common.expression': 'Expression', + 'common.extension': 'Extension', + 'common.fail':'Fail', + 'common.failed':'Failed', 'common.failure': 'Failure', - 'common.type': 'Type', - 'common.direction': 'Direction', - 'common.close': 'Close', - 'common.warning': 'Warning', - 'common.critical': 'Critical', + 'common.file': 'File', + 'common.fileName': 'File Name', + 'common.filter': 'Filter', + 'common.filters': 'Filters', + 'common.finalize': "Finalize", + 'common.free': 'free', + 'common.from.version': 'From Version', + 'common.fullLogPopup.clickToCopy': 'Click to Copy', + 'common.generate.blueprint':'Generate Blueprint', + 'common.group':'Group', + 'common.groups':'Groups', + 'common.hbase':'HBase', + 'common.hide': 'Hide', + 'common.history': 'History', + 'common.host':'Host', + 'common.hostLog.popup.errorLog.value': 'errors-{0}.txt', + 'common.hostLog.popup.logDir.path':'/var/lib/ambari-agent/data/', // TODO, this hardcoded path needs to be removed. + 'common.hostLog.popup.outputLog.value': 'output-{0}.txt', + 'common.hostOrdered': 'Host Ordered', + 'common.hosts':'Hosts', + 'common.hours': "Hours", + 'common.ignore': 'Ignore', + 'common.important.strong': 'Important:', + 'common.important': 'Important', + 'common.include.short': 'Incl', + 'common.include': 'Include', 'common.information': 'Information', - 'common.all':'All', - 'common.success': 'Success', - 'common.fail':'Fail', - 'common.error': 'Error', - 'common.loading': 'Loading', - 'common.search': 'Search', - 'common.confirm': 'Confirm', - 'common.upgrade': 'Upgrade', - 'common.reUpgrade': 'Retry Upgrade', - 'common.reDowngrade': 'Retry Downgrade', - 'common.security':'Security', + 'common.install': "Install", + 'common.installed': 'Installed', + 'common.installRepo.task': ' Install Packages', + 'common.ipAddress':'IP Address', + 'common.issues': 'Issues', + 'common.json': 'Save as JSON', 'common.kerberos':'Kerberos', - 'common.cluster':'Cluster', - 'common.repositories':'Repositories', - 'common.stack.versions':'Stack Versions', - 'common.versions':'Versions', - 'common.upgrade.history':'Upgrade History', - 'common.serviceAccounts': 'Service Accounts', - 'common.add': 'Add', - 'common.edit': 'Edit', - 'common.delete': 'Delete', - 'common.duplicate': 'Duplicate', - 'common.disable': 'Disable', - 'common.enable': 'Enable', - 'common.empty': 'Empty', - 'common.override':'Override', - 'common.undo':'Undo', - 'common.details':'Details', - 'common.stats':'Stats', - 'common.abort': 'Abort', - 'common.aborted': 'Aborted', + 'common.key':'Key', + 'common.keywords': 'Keywods', + 'common.label': 'Label', + 'common.last':'Last', + 'common.latest': 'Latest', + 'common.learnMore':'Learn more', + 'common.levels': 'Levels', + 'common.link': 'Link', + 'common.live': 'Live', + 'common.loadAvg':'Load Avg', + 'common.loading.eclipses': 'Loading...', + 'common.loading': 'Loading', + 'common.logs': 'Logs', + 'common.maint': 'Maint', + 'common.maintenance.task': ' Toggle Maintenance Mode', + 'common.maintenance': 'Maintenance', + 'common.maximum':'Maximum', + 'common.memory':'Memory', + 'common.message':'Message', + 'common.metrics':'Metrics', + 'common.milliseconds': "Milliseconds", + 'common.minutes': "Minutes", 'common.misc': 'Misc', - 'common.userSettings': 'User Settings', - 'common.aboutAmbari': 'About', - 'common.notAvailable': 'Not Available', + 'common.more': 'More...', + 'common.move':'Move', + 'common.mpack': 'Management Pack', + 'common.freeStorage': 'Free Storage', + 'common.noHostsFound': 'No Hosts found.', 'common.na': 'n/a', + 'common.name':'Name', + 'common.new': 'New', + 'common.next': 'Next', + 'common.noData': 'No Data', + 'common.noLink': 'No Links', + 'common.notAvailable': 'Not Available', + 'common.notes': 'Notes', + 'common.nothingToDelete': 'Nothing to delete', + 'common.open':'Open', + 'common.openNewWindow': 'Open in New Window', + 'common.operatingSystem': 'Operating System', 'common.operations': 'Operations', - 'common.backgroundOperations': 'Background Operations', - 'common.startTime': 'Start Time', - 'common.duration': 'Duration', - 'common.reinstall': 'Re-Install', - 'common.revert': 'Revert', - 'common.errorPopup.header': 'An error has been encountered', - 'common.use': 'Use', - 'common.stacks': 'Stacks', - 'common.stack': 'Stack', - 'common.reset': 'Reset', - 'common.reset.default': 'Reset to default', - 'common.resume': 'Resume', - 'common.path': 'Path', - 'common.patch': 'Patch', - 'common.maint': 'Maint', + 'common.optional': 'Optional', + 'common.options': 'Options', + 'common.os': 'OS', + 'common.oss': 'OSs', + 'common.others': 'Others', + 'common.override':'Override', + 'common.overrides': 'Overrides', 'common.package': 'Package', + 'common.passive_state': 'Maintenance Mode', + 'common.password': 'Password', + 'common.patch': 'Patch', + 'common.path': 'Path', + 'common.pause':'Pause', + 'common.persist.error' : 'Error in persisting web client state at ambari server. Server respond with following error message:', + 'common.prerequisites': 'Prerequisites', + 'common.prev':'Prev', + 'common.preview': 'Preview', + 'common.print':'Print', 'common.proceed': 'Proceed', 'common.proceedAnyway': 'Proceed Anyway', - 'common.exitAnyway': 'Exit Anyway', 'common.process': 'Process', - 'common.property': 'Property', - 'common.installed': 'Installed', - 'common.persist.error' : 'Error in persisting web client state at ambari server. Server respond with following error message:', - 'common.update.error' : 'Error in retrieving web client state from ambari server', - 'common.tags': 'Tags', - 'common.important': 'Important', - 'common.important.strong': 'Important:', - 'common.allServices':'All Services', - 'common.move':'Move', - 'common.change': 'Change', - 'common.overrides': 'Overrides', + 'common.progress':'Progress', 'common.properties': 'properties', - 'common.conf.group': 'Configuration Group', - 'common.ignore': 'Ignore', + 'common.property.undefined': "Undefined", + 'common.property': 'Property', + 'common.propertyName': 'Property Name', + 'common.propertyType': 'Property Type', + 'common.public': 'Public', + 'common.quickLinks':'Quick Links', + 'common.rack':'Rack', + 'common.ram':'RAM', + 'common.recommission':'Recommission', + 'common.reDowngrade': 'Retry Downgrade', + 'common.refresh':'Refresh', + 'common.regionServer':'RegionServer', + 'common.reinstall': 'Re-Install', + 'common.remove':'Remove', + 'common.removed': 'Removed', + 'common.repositories':'Repositories', + 'common.repository': 'Repository', + 'common.repositoryType': 'Repository Type', + 'common.reset.default': 'Reset to default', + 'common.reset': 'Reset', 'common.restart': 'Restart', - 'common.discard': 'Discard', - 'common.actions': 'Actions', - 'common.maintenance': 'Maintenance', - 'common.passive_state': 'Maintenance Mode', + 'common.resume': 'Resume', + 'common.retry':'Retry', + 'common.reUpgrade': 'Retry Upgrade', + 'common.revert': 'Revert', + 'common.rollBack':'Rollback', + 'common.rolling.downgrade': 'Rolling Downgrade', + 'common.rolling': 'Rolling', + 'common.running': 'Running', + 'common.save':'Save', + 'common.saveAnyway':'Save Anyway', + 'common.scope': 'Scope', + 'common.search': 'Search', + 'common.seconds': "Seconds", + 'common.security':'Security', 'common.select': 'Select', 'common.selected': 'Selected', - 'common.password': 'Password', - 'common.url': 'URL', - 'common.advanced': 'Advanced', - 'common.download': 'Download', - 'common.current': 'Current', - 'common.additional': 'Additional', - 'common.time.start': 'Start Time', - 'common.time.end': 'End Time', - 'common.hostLog.popup.logDir.path':'/var/lib/ambari-agent/data/', // TODO, this hardcoded path needs to be removed. - 'common.hostLog.popup.outputLog.value': 'output-{0}.txt', - 'common.hostLog.popup.errorLog.value': 'errors-{0}.txt', - 'common.maintenance.task': ' Toggle Maintenance Mode', - 'common.installRepo.task': ' Install Packages', - 'common.used': 'used', - 'common.free': 'free', - 'common.type.string': 'string', - 'common.type.number': 'number', - 'common.author': 'Author', - 'common.notes': 'Notes', - 'common.view': 'View', - 'common.compare': 'Compare', - 'common.latest': 'Latest', - 'common.custom': 'Custom', - 'common.continueAnyway': 'Continue Anyway', - 'common.property.undefined': "Undefined", - 'common.summary': "Summary", - 'common.configs': "Configs", - 'common.configuration': "Configuration", - 'common.unknown': "Unknown", - 'common.install': "Install", - 'common.alertDefinition': "Alert Definition", - 'common.prerequisites': 'Prerequisites', - 'common.finalize': "Finalize", + 'common.servers':'Servers', + 'common.service': 'Service', + 'common.serviceAccounts': 'Service Accounts', + 'common.serviceGroups': 'Service Groups', + 'common.services': 'Services', 'common.severity': "Severity", - 'common.dismiss': "Dismiss", - 'common.stdout': "stdout", + 'common.show':'Show', + 'common.showDetails':'Show Details', + 'common.skip':'Skip', + 'common.stack.versions':'Stack Versions', + 'common.stack': 'Stack', + 'common.stacks': 'Stacks', + 'common.start.time': 'Start Time', + 'common.start':'Start', + 'common.started':'Started', + 'common.startTime': 'Start Time', + 'common.stats':'Stats', + 'common.status':'Status', 'common.stderr': "stderr", + 'common.stdout': "stdout", + 'common.stop':'Stop', + 'common.stopped': 'Stopped', 'common.structuredOut': "structured_out", - 'common.fileName': 'File Name', - 'common.file': 'File', - 'common.days': "Days", - 'common.hours': "Hours", - 'common.minutes': "Minutes", - 'common.seconds': "Seconds", - 'common.milliseconds': "Milliseconds", - 'common.configGroup': 'Config Group', - 'common.expression': 'Expression', - 'common.dataSet': 'Data Set', - 'common.label': 'Label', - 'common.preview': 'Preview', - 'common.options': 'Options', - 'common.scope': 'Scope', - 'common.clone': 'Clone', - 'common.removed': 'Removed', + 'common.success': 'Success', + 'common.summary': "Summary", + 'common.tags': 'Tags', + 'common.taskLog':'Task Log', + 'common.tasks':'Tasks', + 'common.taskTracker':'TaskTracker', 'common.testing': 'Testing', - 'common.noData': 'No Data', - 'common.export': 'Export', - 'common.csv': 'Save as CSV', - 'common.json': 'Save as JSON', - 'common.timestamp': 'Timestamp', - 'common.timezone': 'Timezone', - 'common.loading.eclipses': 'Loading...', - 'common.optional': 'Optional', - 'common.propertyType': 'Property Type', - 'common.running': 'Running', - 'common.stopped': 'Stopped', - 'common.enter': 'Enter', - 'common.timeout.warning.popup.header': 'Automatic Logout', - 'common.timeout.warning.popup.body.before': 'You will be automatically logged out in ', + 'common.time.end': 'End Time', + 'common.time.start': 'Start Time', 'common.timeout.warning.popup.body.after': ' seconds due to inactivity', + 'common.timeout.warning.popup.body.before': 'You will be automatically logged out in ', + 'common.timeout.warning.popup.header': 'Automatic Logout', 'common.timeout.warning.popup.primary': 'Remain Logged In', 'common.timeout.warning.popup.secondary': 'Log Out Now', - 'common.openNewWindow': 'Open in New Window', - 'common.fullLogPopup.clickToCopy': 'Click to Copy', - 'common.nothingToDelete': 'Nothing to delete', - 'common.exclude': 'Exclude', - 'common.include': 'Include', - 'common.exclude.short': 'Excl', - 'common.include.short': 'Incl', - 'common.filters': 'Filters', - 'common.keywords': 'Keywods', - 'common.levels': 'Levels', - 'common.extension': 'Extension', - 'common.logs': 'Logs', - 'common.warn.message': '
{0}
', - 'common.link': 'Link', - 'common.noLink': 'No Links', - 'common.live': 'Live', - 'common.from.version': 'From Version', + 'common.timeRange':'Time Range', + 'common.timestamp': 'Timestamp', + 'common.timezone': 'Timezone', 'common.to.version': 'To Version', - 'common.start.time': 'Start Time', - 'common.end.time': 'End Time', - 'common.rolling': 'Rolling', - 'common.express': 'Express', - 'common.hostOrdered': 'Host Ordered', - 'common.repository': 'Repository', - 'common.repositoryType': 'Repository Type', - 'common.rolling.downgrade': 'Rolling Downgrade', - 'common.express.downgrade': 'Express Downgrade', + 'common.type.number': 'number', + 'common.type.string': 'string', + 'common.type': 'Type', + 'common.undo':'Undo', + 'common.unknown': "Unknown", + 'common.update.error' : 'Error in retrieving web client state from ambari server', + 'common.upgrade.history':'Upgrade History', + 'common.upgrade': 'Upgrade', + 'common.url': 'URL', + 'common.use': 'Use', + 'common.used': 'used', + 'common.user': 'User', + 'common.users': 'Users', + 'common.userSettings': 'User Settings', + 'common.value': 'Value', + 'common.verification': 'Verification', + 'common.version':'Version', + 'common.versions':'Versions', + 'common.view': 'View', + 'common.viewLog': 'View Log', 'common.views': 'Views', - 'common.critical.error': 'Critical', + 'common.warn.message': '
{0}
', + 'common.warning': 'Warning', 'common.with': 'with', - 'common.propertyName': 'Property Name', + 'common.zookeeper':'ZooKeeper', + 'common.critical.error': 'Critical', 'models.alert_instance.tiggered.verbose': "Occurred on {0}
Checked on {1}", 'models.alert_definition.triggered.verbose': "Occurred on {0}", @@ -429,6 +440,8 @@ Em.I18n.translations = { 'hostPopup.recommendation.beforeDecommission': '{0} Maintenance Mode is pre required for decommissioning.', 'question.sure':'Are you sure?', + 'question.sure.regenerateKeytab.host': 'Are you sure you want to regenerate keytab file operations for a {0} host?', + 'question.sure.regenerateKeytab.service': 'Are you sure you want to regenerate keytab file operations for a {0} service?', 'question.sure.restart':'Are you sure you want to restart {0}?', 'question.sure.start':'Are you sure you want to start {0}?', 'question.sure.stop':'Are you sure you want to stop {0}?', @@ -441,8 +454,6 @@ Em.I18n.translations = { 'question.sure.startAll':'Are you sure you want to start all the components?', 'question.sure.stopAll':'Are you sure you want to stop all the components?', 'question.sure.restartAll':'Are you sure you want to restart all the components?', - 'question.sure.regenerateKeytab.service': 'Are you sure you want to regenerate keytab file operations for a {0} service?', - 'question.sure.regenerateKeytab.host': 'Are you sure you want to regenerate keytab file operations for a {0} host?', 'popup.highlight':'click to highlight', 'popup.confirmation.commonHeader':'Confirmation', @@ -591,11 +602,15 @@ Em.I18n.translations = { 'installer.header':'Cluster Install Wizard', 'installer.navigation.warning.header':'Navigation Warning', - + 'installer.navigation.warning':'If you make changes to a previous step you will lose any changes saved in subsequent steps.', + 'installer.warning.changes':'If you make changes to this step you will lose any changes saved in subsequent steps.', 'installer.noHostsAssigned':'No host assigned', 'installer.slaveComponentHosts.selectHosts':'select hosts for this group', 'installer.slaveComponentHostsPopup.header':'Select which hosts should belong to which {0} group', + 'installer.error.mpackServiceInfo': 'Failed to load mpack service info.', + 'installer.error.mpackStackInfo': 'Failed to load stack info.', + 'installer.controls.slaveComponentGroups':' Groups', 'installer.controls.serviceConfigPopover.title':'{0}
{1}', 'installer.controls.checkConnection.popover':'This action will check accessibility of {0} host and port from Ambari Server host', @@ -605,8 +620,8 @@ Em.I18n.translations = { 'installer.controls.slaveComponentChangeGroupName.error':'group with this name already exist', 'installer.step0.header':'Get Started', - 'installer.step0.body.header':'Get Started', - 'installer.step0.body':'This wizard will walk you through the cluster installation process. First, start by naming your new cluster.', + 'installer.step0.body.header':'Name cluster', + 'installer.step0.body':'This wizard will walk you through the cluster installation process. First, name your new cluster.', 'installer.step0.clusterName':'Name your cluster', 'installer.step0.clusterName.tooltip.title':'Cluster Name', 'installer.step0.clusterName.tooltip.content':'Enter a unique cluster name.', @@ -614,6 +629,65 @@ Em.I18n.translations = { 'installer.step0.clusterName.error.tooLong':'Cluster Name is too long', 'installer.step0.clusterName.error.whitespace':'Cluster Name cannot contain whitespace', 'installer.step0.clusterName.error.specialChar':'Cluster Name cannot contain special characters', + + 'installer.selectMpacks.header': 'Select Management Packs', + 'installer.selectMpacks.body.header': 'Select Management Packs', + 'installer.selectMpacks.body.header.useCases': 'Select the use cases that best describe your needs, and we\'ll suggest the best Management Packs and Services.', + 'installer.selectMpacks.body.header.mpacks': 'Management Packs', + 'installer.selectMpacks.body.header.services': 'Services', + 'installer.selectMpacks.body.selected.header': 'Selected Management Packs', + 'installer.selectMpacks.loadRegistryFailed': 'Could not load available management packs. The software registry may not be available.', + 'installer.selectMpacks.noUsecasesAvailable': 'No use cases are available.', + 'installer.selectMpacks.noMpacksAvailable': 'No management packs are available.', + 'installer.selectMpacks.noServicesAvailable': 'No services are available.', + 'installer.selectMpacks.noMpacksSelected': 'No management packs selected.', + 'installer.selectMpacks.noRecommendationAvailable': 'No management packs support the selected use cases. Please change your selection.', + 'installer.selectMpacks.getRecommendationFailed': 'Could not load management macks supporting the selected use cases. The software registry may not be available.', + 'installer.selectMpacks.basicMode': 'Use Cases', + 'installer.selectMpacks.advancedMode': 'Customize', + 'installer.selectMpacks.changeMode': 'Change Selection Mode', + 'installer.selectMpacks.basicModeMessage': 'Customizing your selections allows you to add or remove individual management packs and services. Continue?', + 'installer.selectMpacks.advancedModeMessage': 'If you go back to use case selection, all current selections will be removed. Continue?', + 'installer.selectMpacks.basicModeHelp': 'This step lets you choose the use case(s) that fit your needs and the management packs that fulfill those use cases will be selected for you; however, if you would rather choose individual management packs and/or services directly, click this button. Be aware that if you return to use case selection mode, any selections you have made will be removed.', + 'installer.selectMpacks.advancedModeHelp': 'By clicking this button, you can return to use case selection mode. However, any existing selections will be removed.', + 'installer.selectMpacks.filterMpacks': 'Search management packs', + 'installer.selectMpacks.filterServices': 'Search services', + + 'installer.configureDownload.header': 'Configure Download', + 'installer.configureDownload.body.title': 'Choose download method', + 'installer.configureDownload.body.description': 'Using Public Repositories requires an internet connection. Choose Local Repositories to change the download locations to something other than the public repositories, such as a repositories on your network.', + 'installer.configureDownload.publicRepo': 'Public Repositories', + 'installer.configureDownload.customRepo': 'Local Repositories', + 'installer.configureDownload.publicRepo.description': 'Software will be downloaded from public repositories.', + 'installer.configureDownload.customRepo.description': 'Software will be downloaded from locations you specify.', + 'installer.configureDownload.useProxy': 'Use Proxy', + 'installer.configureDownload.proxyUrl': 'Proxy URL', + 'installer.configureDownload.proxyUrl.placeholder': 'http://server:port', + 'installer.configureDownload.proxyAuth': 'Authentication', + 'installer.configureDownload.useRedHatSatellite': 'Use Red Hat Satellite/Spacewalk', + + 'installer.customMpackRepos.header': 'Set Management Pack Locations', + 'installer.customMpackRepos.body.title': 'Customize management pack locations', + 'installer.customMpackRepos.body.description': 'Get the management packs from their public repos and add them to your local repo, then update the download URLs below.', + 'installer.customMpackRepos.publicRepo': 'Public Link', + 'installer.customMpackRepos.customRepo': 'Download URL', + + 'installer.downloadMpacks.header': 'Download Management Packs', + 'installer.downloadMpacks.body.title': 'Download and validate management packs', + 'installer.downloadMpacks.body.description': 'Ambari is downloading the management packs and validating their contents.', + 'installer.downloadMpacks.failure.default': 'Downloading management pack failed for unknown reasons.', + + 'installer.customProductRepos.header': 'Set Product Locations', + 'installer.customProductRepos.body.title': 'Customize product locations', + 'installer.customProductRepos.body.description': 'Get the products from their public repos and add them to your local repo, then update the download URLs below.', + 'installer.customProductRepos.publicRepo': 'Public Link', + 'installer.customProductRepos.customRepo': 'Download URL', + 'installer.customProductRepos.addRemoveOs': 'Add/Remove OS', + 'installer.customProductRepos.noSelectedOs': 'Please select an operating system.', + + 'installer.verifyProducts.header': 'Verify Products', + 'installer.verifyProducts.body.title': 'Verify products', + 'installer.verifyProducts.body.description': 'Ambari is verifying the product repositories.', 'installer.step1.header':'Select Version', 'installer.step1.body':'Select the software version and method of delivery for your cluster.', @@ -676,10 +750,11 @@ Em.I18n.translations = { 'installer.step1.checkAtLeastOneAttention': 'Attention: Please check at least one repository.', 'installer.step1.retryRepoUrls': 'Click here to retry.', - 'installer.step2.header':'Install Options', - 'installer.step2.body':'Enter the list of hosts to be included in the cluster and provide your SSH key.', - 'installer.step2.targetHosts':'Target Hosts', - 'installer.step2.targetHosts.info':'Enter a list of hosts using the Fully Qualified Domain Name (FQDN), one per line', + 'installer.step2.header':'Add Hosts', + 'installer.step2.registration.body1' : 'Ambari Agents can be automatically installed using SSH, or they can be manually installed and registered with the Ambari Server.', + 'installer.step2.registration.body2' : 'Please choose the installation type below:', + 'installer.step2.enterHosts':'Enter Hosts', + 'installer.step2.enterHosts.info':'Enter a list of hosts using the Fully Qualified Domain Name (FQDN), one per line', 'installer.step2.hostPattern.tooltip.title':'Pattern Expressions', 'installer.step2.hostPattern.tooltip.content':'You can use pattern expressions to specify a number of target hosts. For example, to specify host01.domain thru host10.domain, enter host[01-10].domain in the target hosts textarea.', 'installer.step2.hostName.error.required':'You must specify at least one host name', @@ -710,15 +785,18 @@ Em.I18n.translations = { 'installer.step2.javaHome.tooltip.title' : 'JAVA_HOME', 'installer.step2.javaHome.tooltip.content' : 'Path to 64-bit JAVA_HOME. /usr/jdk/jdk1.6.0_31 is the default used by Ambari. You can override this to a specific path that contains the JDK.
Note: the path must be valid on ALL hosts in your cluster.', 'installer.step2.javaHome.tooltip.placeholder' : '/usr/jdk/jdk1.6.0_31', + 'installer.step2.automaticInstall' : 'Register Agents automatically', 'installer.step2.automaticInstall.tooltip.title':'automatic registration', 'installer.step2.automaticInstall.tooltip.content':'Ambari will automatically install and register the Ambari Agent on each host prior to the cluster installation.', 'installer.step2.useSsh.provide' : 'Provide your', 'installer.step2.useSsh.provide_id_rsa' : ' to automatically register hosts', 'installer.step2.useSsh.tooltip.title':'SSH Private Key', 'installer.step2.useSsh.tooltip.content':'The SSH Private Key File is used to connect to the target hosts in your cluster to install the Ambari Agent.', + 'installer.step2.useSsh' : 'Register Agents Using SSH', 'installer.step2.install.perform':'Perform', 'installer.step2.install.perform_on_hosts':'on hosts', 'installer.step2.install.without_ssh':' and do not use SSH', + 'installer.step2.manualInstall' : 'Register Agents Manually', 'installer.step2.manualInstall.tooltip.title':'manual registration', 'installer.step2.manualInstall.tooltip.content':'Manually registering the Ambari Agent on each host eliminates the need for SSH and should be performed prior to continuing cluster installation.', 'installer.step2.manualInstall.tooltip.content_no_ssh':'Manually registering the Ambari Agent on each host should be performed prior to continuing cluster installation.', @@ -745,10 +823,18 @@ Em.I18n.translations = { 'installer.step2.skipHostChecks.label': 'Skip host checks', 'installer.step2.skipHostChecks.popup.header': 'Warning', 'installer.step2.skipHostChecks.popup.body': 'By skipping host checks, Ambari will not check and warn if any issues with the host are identified and the host will be added to the cluster as is.', + 'installer.step2.parsedHostsPlaceholder' : '{0} HOSTS ADDED', + 'installer.step2.preRegistered.hostCount': 'We found {0} registered hosts.', + 'installer.step2.noHostsFound.header': 'Please follow the following steps to register hosts manually. Refresh the table to see the updated list.', + 'installer.step2.noHostsFound.step1': '1. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et.', + 'installer.step2.noHostsFound.step2': '2. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.', + 'installer.step2.noHostsFound.step3': '3. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.', + 'installer.step2.noHostsFound.step4': '4. Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'installer.step2.noHostsFound.learn': 'Learn More', + 'installer.step2.refresh.table': 'REFRESH TABLE', 'installer.step3.header':'Confirm Hosts', - 'installer.step3.body':'Registering your hosts.
' + - 'Please confirm the host list and remove any hosts that you do not want to include in the cluster.', + 'installer.step3.body':'Registering your hosts. Please confirm the host list and remove any hosts that you do not want to include in the cluster.', 'installer.step3.hostLog.popup.header':'Registration log for {0}', 'installer.step3.hosts.remove.popup.header':'Remove Hosts', 'installer.step3.hosts.remove.popup.body':'Are you sure you want to remove the selected host(s)?', @@ -848,8 +934,7 @@ Em.I18n.translations = { 'installer.step3.hostWarningsPopup.moreHosts':'{0} more hosts...
Click on link to view all hosts.', 'installer.step3.hostWarningsPopup.allHosts':'List of hosts', 'installer.step3.hostWarningsPopup.rerunChecks':'Rerun Checks', - 'installer.step3.hostWarningsPopup.hostHasWarnings':'Host checks failed on some of your hosts. It is highly recommended that you fix these problems first before proceeding to prevent potentially major problems with cluster installation. Are you sure you want to ignore these warnings and proceed?', - 'installer.step3.hostWarningsPopup.hostHasWarnings.header':'Host Check Warning', + 'installer.step3.hostWarningsPopup.hostHasWarnings':'Warning: Host checks failed on some of your hosts. It is highly recommended that you fix these problems first before proceeding to prevent potentially major problems with cluster installation. Are you sure you want to ignore these warnings and proceed?', 'installer.step3.warningsWindow.allHosts':'Warnings across all hosts', 'installer.step3.warningsWindow.warningsOn':'Warnings on ', 'installer.step3.warningsWindow.directoriesAndFiles':'DIRECTORIES AND FILES', @@ -869,8 +954,6 @@ Em.I18n.translations = { 'installer.step3.warning.registeredHosts': '{0} Other Registered Hosts', 'installer.step3.warning.loading':'Please wait while the hosts are being checked for potential problems...', 'installer.step3.registeredHostsPopup': 'These are the hosts that have registered with the server, but do not appear in the list of hosts that you are adding.', - 'installer.step3.removeSelected':'Remove Selected', - 'installer.step3.retryFailed':'Retry Failed', 'installer.step3.hosts.status.registering':'Registering', 'installer.step3.hosts.status.installing':'Installing', 'installer.step3.hosts.bootLog.failed':'\nRegistration with the server failed.', @@ -882,16 +965,16 @@ Em.I18n.translations = { 'installer.step4.header':'Choose Services', 'installer.step4.body':'Choose which services you want to install on your cluster.', - 'installer.step4.headerFS':'Choose File System', - 'installer.step4.bodyFS':'Choose which file system you want to install on your cluster.', 'installer.step4.fsCheck.popup.header':'File System Required', 'installer.step4.fsCheck.popup.body':'You did not select a File System but one is required. We will automatically add {0}. Is this OK?', + 'installer.step4.bodyFS':'Choose which file system you want to install on your cluster.', + 'installer.step4.hcfs.displayName':'a Hadoop Compatible File System', + 'installer.step4.headerFS':'Choose File System', 'installer.step4.multipleDFS.popup.header':'Multiple File Systems Selected', 'installer.step4.multipleDFS.popup.body':'You selected more than one file system. We will automatically select only {0}. Is this OK?', 'installer.step4.serviceCheck.popup.header':'{0} Needed', - 'installer.step4.serviceCheck.popup.body':'You did not select {0}, but it is needed by other services you selected. We will automatically add {1}. Is this OK?', + 'installer.step4.serviceCheck.popup.body':'You did not select {0}, but it is needed by other services you selected. We will automatically add {0}. Is this OK?', 'installer.step4.serviceCheck.popup.body.multiOptions':'You did not select {0}, but it is needed by other services you selected. Select a compatible service from the following list: {1}', - 'installer.step4.hcfs.displayName':'a Hadoop Compatible File System', 'installer.step4.limitedFunctionality.popup.header':'Limited Functionality Warning', 'installer.step4.ambariMetricsCheck.popup.body':'Ambari Metrics collects metrics from the cluster and makes them available to Ambari. If you do not install Ambari Metrics service, metrics will not be accessible from Ambari. Are you sure you want to proceed without Ambari Metrics?', 'installer.step4.ambariInfraCheck.popup.body':'Since Ambari Infra is not selected, you must supply your own Solr to make Atlas work. Are you sure you want to proceed?', @@ -929,7 +1012,10 @@ Em.I18n.translations = { 'installer.step6.wizardStep6Host.title':'master components hosted on {0}', 'installer.step6.addHostWizard.body':'Assign HBase master and ZooKeeper server.', 'installer.step6.error.mustSelectOneForSlaveHost': 'You must assign at least one slave/client component to each host with no master component', + 'installer.step6.validationSlavesAndClients.hasIssues': 'Your slave and client assignment has issues. ', 'installer.step6.validationSlavesAndClients.click': 'Click', + 'installer.step6.validationSlavesAndClients.forDetails': ' for details.', + 'installer.step6.validationSlavesAndClients.popup.header': 'Assign Slaves and Clients Issues', 'installer.step6.validationSlavesAndClients.popup.body': 'Assignment of slave and client components has the following issues', 'installer.step6.validationIssuesAttention.header': 'Validation Issues', 'installer.step6.validationIssuesAttention': 'Slave and Client component assignments have issues that need attention.', @@ -1284,9 +1370,9 @@ Em.I18n.translations = { 'admin.kerberos.wizard.step1.option.ad.condition.4': 'Active Directory administrative credentials with delegated control of “Create, delete, and manage user accounts” on the previously mentioned User container are on-hand.', 'admin.kerberos.wizard.step1.option.ad.condition.5': 'The Java Cryptography Extensions (JCE) have been setup on the Ambari Server host and all hosts in the cluster.', 'admin.kerberos.wizard.step1.option.ipa': 'Existing IPA', - 'admin.kerberos.wizard.step1.option.ipa.condition.1': 'All cluster hosts are joined to the IPA domain and hosts are registered in DNS', - 'admin.kerberos.wizard.step1.option.ipa.condition.2': 'A password policy is in place that sets no expiry for created principals', - 'admin.kerberos.wizard.step1.option.ipa.condition.3': 'If you do not plan on using Ambari to manage the krb5.conf, ensure the following is set in each krb5.conf file in your cluster: default_ccache_name = /tmp/krb5cc_%{uid}', + 'admin.kerberos.wizard.step1.option.ipa.condition.1': 'Cluster hosts are joined to the IPA domain and hosts are registered in DNS', + 'admin.kerberos.wizard.step1.option.ipa.condition.2': 'A password policy in place that sets no expiry for created principals', + 'admin.kerberos.wizard.step1.option.ipa.condition.3': 'The ipa managed krb5.conf sets default_ccache_name = /tmp/krb5cc_%{uid}', 'admin.kerberos.wizard.step1.option.ipa.condition.4': 'The Java Cryptography Extensions (JCE) have been setup on the Ambari Server host and all hosts in the cluster.', 'admin.kerberos.wizard.step1.prerequisites.label': 'Following prerequisites needs to be checked to progress ahead in the wizard.', 'admin.kerberos.wizard.step2.info.body': 'Please configure kerberos related properties.', @@ -1887,6 +1973,23 @@ Em.I18n.translations = { "
  • DO NOT enable / disable HA
  • " + "
  • DO NOT make any drastic changes to service configurations
  • " + "You MUST continue the {0} and finalize BEFORE performing ANY significant changes to the cluster.", + + 'admin.serviceGroups.title': "Management Packs", + 'admin.serviceGroups.createUpgradePlan': "Create Upgrade Plan", + 'admin.serviceGroups.addServiceGroup': "Add Management Pack", + 'admin.serviceGroups.removeOldVersions': "Remove Old Versions", + 'admin.serviceGroups.startUpgradePlan.title': "Upgrade Process", + 'admin.serviceGroups.startUpgradePlan.description': "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.", + 'admin.serviceGroups.upgradePlan': "Upgrade Plan", + 'admin.serviceGroups.startUpgradePlan.upgradePlanDescription': "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.", + 'admin.serviceGroups.startUpgradePlan.prerequisitesDescription': "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.", + 'admin.serviceGroups.startUpgradePlan.installDescription': "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.", + 'admin.serviceGroups.startUpgradePlan.upgradeDescription': "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.", + 'admin.serviceGroups.startUpgradePlan.buttonLabel': 'Create Upgrade Plan', + 'admin.serviceGroups.upgradeStatus.button.prerequisites': 'Review Prerequisites', + 'admin.serviceGroups.upgradeStatus.button.install': 'Install Products', + 'admin.serviceGroups.upgradeStatus.button.upgrade': 'Perform Upgrade', + 'admin.stackUpgrade.downgrade.proceed': "Proceed with Downgrade", 'admin.stackUpgrade.downgrade.body': "Are you sure you wish to abort the upgrade process and downgrade to {0}?", 'admin.stackUpgrade.downgrade.retry.body': "Are you sure you wish to retry downgrade to {0}?", @@ -1933,6 +2036,15 @@ Em.I18n.translations = { 'admin.stackUpgrade.dialog.notActive': "Waiting to execute the next task...", 'admin.stackUpgrade.dialog.prepareUpgrade.header': "Preparing the Upgrade...", 'admin.stackUpgrade.dialog.skipped.failures':'There were automatically skipped failed steps. Please resolve each failure before continuing with the upgrade.', + + 'admin.createUpgradePlan.wizard.header': 'Create Upgrade Plan', + 'admin.createUpgradePlan.wizard.downloadOptions.header': 'Download Options', + 'admin.createUpgradePlan.wizard.selectUpgradeOptions.header': 'Select Upgrade Options', + 'admin.createUpgradePlan.wizard.downloadMpacks.header': 'Download Mpacks', + 'admin.createUpgradePlan.wizard.reviewConfigs.header': 'Review Configs', + 'admin.createUpgradePlan.wizard.selectUpgradeType.header': 'Select Upgrade Type', + 'admin.createUpgradePlan.wizard.upgradeSummary.header': 'Upgrade Summary', + 'services.service.start':'Start', 'services.service.stop':'Stop', 'services.service.metrics':'Metrics', @@ -2271,7 +2383,7 @@ Em.I18n.translations = { 'services.service.config.configHistory.leftArrow.tooltip': 'Show later versions', 'services.service.config.configHistory.dismissIcon.tooltip': 'Dismiss', 'services.service.config.configHistory.makeCurrent.message': 'Created from service config version {0}', - 'services.service.config.configHistory.comparing': 'Comparing Changes', + 'services.service.config.configHistory.comparing': 'Comparing', 'services.service.config.setRecommendedValue': 'Set Recommended', 'services.service.config.database.msg.jdbcSetup.detailed': 'To use {0} with Hive, you must ' + 'download the {4} from {0}. Once downloaded to the Ambari Server host, run:
    ' + @@ -2628,6 +2740,7 @@ Em.I18n.translations = { 'hosts.bulkOperation.confirmation.add.component':'{0} will be added to the following hosts.', 'hosts.bulkOperation.confirmation.add.component.skip':'{0} already installed.', 'hosts.bulkOperation.confirmation.add.component.nothingToDo.body': 'All the selected hosts have {0} installed already.', + 'hosts.bulkOperation.confirmation.cannot.add1': 'The following hosts will be skipped (expand for reason):', 'hosts.bulkOperation.confirmation.cannot.add2': '{0} cannot be added to the following hosts (expand for reason):', 'hosts.bulkOperation.deleteHosts.nonDeletableComponents': 'Deletion of the following components is not supported: {0}', @@ -2638,11 +2751,12 @@ Em.I18n.translations = { 'hosts.bulkOperation.deleteHosts.confirm.delete': 'The following hosts will be deleted:', 'hosts.bulkOperation.deleteHosts.cannot.delete1':'The following hosts will be skipped (expand for reason):', 'hosts.bulkOperation.deleteHosts.cannot.delete2':'Selected hosts cannot be deleted (expand for reason)', + 'hosts.bulkOperation.deleteHosts.confirmation.header':'Confirm Bulk Delete Hosts', 'hosts.bulkOperation.deleteHosts.confirmation.body.msg1': 'Please note: Once removed, Ambari will ignore future communications from these hosts. As part of the removal process, packages will not be removed, so please do not attempt to manually start the services on the host once they have been removed from Ambari. If you wish to re-add the hosts to the cluster, please completely clean the hosts before attempting to add them.', 'hosts.bulkOperation.deleteHosts.confirmation.body.msg2': 'To ensure they are completely removed from Ambari database,', 'hosts.bulkOperation.deleteHosts.confirmation.body.msg3': 'please make sure the Ambari Agent process is completely stopped on these hosts before proceeding.', - 'hosts.bulkOperation.deleteHosts.result.header':'Delete Hosts', + 'hosts.bulkOperation.deleteHosts.result.header': 'Delete Hosts', 'hosts.bulkOperation.deleteHosts.result.body': 'The following hosts were successfully deleted:', 'hosts.bulkOperation.confirmation.delete.component.cannot1': 'The following hosts will be skipped (expand for reason):', 'hosts.bulkOperation.confirmation.delete.component.cannot2': '{0} cannot be deleted from the selected hosts:', @@ -2651,6 +2765,7 @@ Em.I18n.translations = { 'hosts.bulkOperation.confirmation.delete.component.nothingToDo.notStopped': '{0} not Stopped on all selected hosts', 'hosts.bulkOperation.confirmation.delete.component.nothingToDo.notInstalled': '{0} not installed in any of the selected hosts', 'hosts.bulkOperation.confirmation.delete.component.skip':'The following hosts will be skipped', + 'hosts.bulkOperation.delete.component.result.header':'Delete Components', 'hosts.bulkOperation.confirmation.delete.component.notStopped': '{0} not Stopped', 'hosts.bulkOperation.confirmation.delete.component.notInstalled': '{0} not Installed', @@ -3147,7 +3262,7 @@ Em.I18n.translations = { 'tableView.filters.all': 'All', 'tableView.filters.filtered': 'Filtered', 'tableView.filters.clearFilters': 'Clear filters', - 'tableView.filters.itemsPerPage': 'Items per page:', + 'tableView.filters.rowsPerPage': 'Rows per page:', 'tableView.filters.paginationInfo': '{0} - {1} of {2}', 'tableView.filters.clearAllFilters': 'clear filters', 'tableView.filters.showAll': 'Show All', diff --git a/ambari-web/app/mixins/common/configs/enhanced_configs.js b/ambari-web/app/mixins/common/configs/enhanced_configs.js index f870fc69e45..3072cb5cffd 100644 --- a/ambari-web/app/mixins/common/configs/enhanced_configs.js +++ b/ambari-web/app/mixins/common/configs/enhanced_configs.js @@ -279,12 +279,36 @@ App.EnhancedConfigsMixin = Em.Mixin.create(App.ConfigWithOverrideRecommendationP * @returns {{recommend: string, hosts: *, services: *, changed_configurations: *}} */ getConfigRecommendationsParams: function(updateDependencies, changedConfigs) { - return { + const params = { recommend: updateDependencies ? 'configuration-dependencies' : 'configurations', hosts: this.get('hostNames'), services: this.get('serviceNames'), changed_configurations: updateDependencies ? changedConfigs : undefined }; + + //TODO - mpacks: Hard coded to use first mpack. Change when we are installing multiple mpacks. + const selectedMpacks = this.get('content.selectedMpacks'); + + if (selectedMpacks) { + params.stackName = selectedMpacks[0].name; + params.stackVersion = selectedMpacks[0].version; + } + + return params; + }, + + /** + * Get the url to use to request the correct stack version from stack advisor. + * Falls back to using the old global config value if necessary. + * + * @param {object} options Should include stackName and stackVersion properties, which should refer to the mpack being installed. + */ + getStackVersionUrl: function getStackVersionUrl(options) { + if (options.stackName && options.stackVersion) { + return '/stacks/' + options.stackName + '/versions/' + options.stackVersion; + } + + return App.get('stackVersionURL'); }, getRecommendationsRequest: function (dataToSend, callback) { @@ -294,8 +318,14 @@ App.EnhancedConfigsMixin = Em.Mixin.create(App.ConfigWithOverrideRecommendationP name: 'config.recommendations', sender: self, data: { - stackVersionUrl: App.get('stackVersionURL'), - dataToSend: dataToSend + stackVersionUrl: this.getStackVersionUrl(dataToSend), + //exclude stackName and stackVersion from dataToSend + dataToSend: { + recommend: dataToSend.recommend, + hosts: dataToSend.hosts, + services: dataToSend.services, + changed_configurations: dataToSend.changed_configurations + } }, success: 'loadRecommendationsSuccess', error: 'loadRecommendationsError', diff --git a/ambari-web/app/mixins/common/hosts/host_component_recommendation_mixin.js b/ambari-web/app/mixins/common/hosts/host_component_recommendation_mixin.js index b0d58e76d31..0c6113d33af 100644 --- a/ambari-web/app/mixins/common/hosts/host_component_recommendation_mixin.js +++ b/ambari-web/app/mixins/common/hosts/host_component_recommendation_mixin.js @@ -90,6 +90,20 @@ App.HostComponentRecommendationMixin = Em.Mixin.create(App.BlueprintMixin, { return res; }, + /** + * Get the url to use to request the correct stack version from stack advisor. + * Falls back to using the old global config value if necessary. + * + * @param {object} options Should include stackName and stackVersion properties, which should refer to the mpack being installed. + */ + getStackVersionUrl(options) { + if (options.stackName && options.stackVersion) { + return '/stacks/' + options.stackName + '/versions/' + options.stackVersion; + } + + return App.get('stackVersionURL'); + }, + /** * Returns request data for recommendation request * @param {HostComponentRecommendationOptions} options @@ -99,7 +113,7 @@ App.HostComponentRecommendationMixin = Em.Mixin.create(App.BlueprintMixin, { getRecommendationRequestData: function(options) { return { recommend: 'host_groups', - stackVersionUrl: App.get('stackVersionURL'), + stackVersionUrl: this.getStackVersionUrl(options), hosts: options.hosts, services: options.services, recommendations: options.blueprint || this.getComponentsBlueprint(options.components) diff --git a/ambari-web/app/mixins/common/hosts/host_component_validation_mixin.js b/ambari-web/app/mixins/common/hosts/host_component_validation_mixin.js index 538f83c5f02..a8b1cd47447 100644 --- a/ambari-web/app/mixins/common/hosts/host_component_validation_mixin.js +++ b/ambari-web/app/mixins/common/hosts/host_component_validation_mixin.js @@ -70,6 +70,20 @@ App.HostComponentValidationMixin = Em.Mixin.create(App.BlueprintMixin, { return res; }, + /** + * Get the url to use to request the correct stack version from stack advisor. + * Falls back to using the old global config value if necessary. + * + * @param {object} options Should include stackName and stackVersion properties, which should refer to the mpack being installed. + */ + getStackVersionUrl(options) { + if (options.stackName && options.stackVersion) { + return '/stacks/' + options.stackName + '/versions/' + options.stackVersion; + } + + return App.get('stackVersionURL'); + }, + /** * Returns request data for validation request * @method getHostComponentValidationParams @@ -77,7 +91,7 @@ App.HostComponentValidationMixin = Em.Mixin.create(App.BlueprintMixin, { */ getHostComponentValidationParams: function(options) { return { - stackVersionUrl: App.get('stackVersionURL'), + stackVersionUrl: this.getStackVersionUrl(options), hosts: options.hosts, services: options.services, validate: 'host_groups', diff --git a/ambari-web/app/mixins/wizard/assign_master_components.js b/ambari-web/app/mixins/wizard/assign_master_components.js index c9577f87b07..4fa8d00826f 100644 --- a/ambari-web/app/mixins/wizard/assign_master_components.js +++ b/ambari-web/app/mixins/wizard/assign_master_components.js @@ -309,6 +309,8 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A clearRecommendations: function() { if (this.get('content.recommendations')) { this.set('content.recommendations', null); + } + if (this.get('recommendations')) { this.set('recommendations', null); } }, @@ -527,7 +529,12 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A if (self.get('recommendations')) { self.set('backFromNextStep', true); } + + //TODO - mpacks: Hard-coding to only ask for recommendations for first mpack. Need to change this when we are installing multiple mpacks. + const selectedMpacks = self.get('content.selectedMpacks'); self.getRecommendedHosts({ + stackName: selectedMpacks[0].name, + stackVersion: selectedMpacks[0].version, hosts: self.getHosts() }).then(function () { self.loadStepCallback(self.createComponentInstallationObjects(), self); @@ -675,7 +682,7 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A */ createComponentInstallationObjects: function() { var stackMasterComponentsMap = {}, - masterHosts = this.get('content.masterComponentHosts') || this.get('masterComponentHosts'), //saved to local storage info + masterHosts = this.get('content.masterComponentHosts'), servicesToAdd = (this.get('content.services')|| []).filterProperty('isSelected').filterProperty('isInstalled', false).mapProperty('serviceName'), recommendations = this.get('recommendations'), resultComponents = [], @@ -955,13 +962,23 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A assignHostToMaster: function (componentName, selectedHost, serviceComponentId) { var flag = this.isHostNameValid(componentName, selectedHost); var component; + const stepName = this.get('stepName'); + this.updateIsHostNameValidFlag(componentName, serviceComponentId, flag); + if (serviceComponentId) { component = this.get('selectedServicesMasters').filterProperty('component_name', componentName).findProperty("serviceComponentId", serviceComponentId); - if (component) component.set("selectedHost", selectedHost); - } - else { + if (component) { + component.set("selectedHost", selectedHost); + if (stepName) { + this.get('wizardController').setStepUnsaved(stepName); + } + } + } else { this.get('selectedServicesMasters').findProperty("component_name", componentName).set("selectedHost", selectedHost); + if (stepName) { + this.get('wizardController').setStepUnsaved(stepName); + } } }, @@ -1093,6 +1110,12 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A }); this.incrementProperty('rebalanceComponentHostsCounter'); this.toggleProperty('hostNameCheckTrigger'); + + const stepName = this.get('stepName'); + if (stepName) { + this.get('wizardController').setStepUnsaved(stepName); + } + return true; } return false;//if no more zookeepers can be added @@ -1130,6 +1153,12 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A }); this.incrementProperty('rebalanceComponentHostsCounter'); this.toggleProperty('hostNameCheckTrigger'); + + const stepName = this.get('stepName'); + if (stepName) { + this.get('wizardController').setStepUnsaved(stepName); + } + return true; }, @@ -1143,10 +1172,14 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A } this.set('validationInProgress', true); - + + //TODO - mpacks: Hard coded to request first mpack only. Must be changed when we are installing multiple mpacks. + const selectedMpacks = this.get('content.selectedMpacks'); // load recommendations with partial request this.getRecommendedHosts({ hosts: hostNames, + stackName: selectedMpacks[0].name, + stackVersion: selectedMpacks[0].version, components: this.getCurrentComponentHostMap() }).then(function() { self.validateSelectedHostComponents({ diff --git a/ambari-web/app/mixins/wizard/wizard_menu_view.js b/ambari-web/app/mixins/wizard/wizard_menu_view.js index 8efebee9b80..52a34dd736c 100644 --- a/ambari-web/app/mixins/wizard/wizard_menu_view.js +++ b/ambari-web/app/mixins/wizard/wizard_menu_view.js @@ -34,7 +34,13 @@ App.WizardMenuMixin = Em.Mixin.create({ isStepDisabled: function (stepName) { let index = this.get('controller').getStepIndex(stepName); - return this.get('controller.isStepDisabled').findProperty('step', index).get('value'); + let step = this.get('controller.isStepDisabled').findProperty('step', index); + + if (step) { + return step.get('value'); + } + + return false; }, isStepCompleted(stepName) { @@ -42,28 +48,50 @@ App.WizardMenuMixin = Em.Mixin.create({ return this.get('controller.currentStep') > index; }, - isStep0Disabled: isStepDisabled(0), - isStep1Disabled: isStepDisabled(1), - isStep2Disabled: isStepDisabled(2), - isStep3Disabled: isStepDisabled(3), - isStep4Disabled: isStepDisabled(4), - isStep5Disabled: isStepDisabled(5), - isStep6Disabled: isStepDisabled(6), - isStep7Disabled: isStepDisabled(7), - isStep8Disabled: isStepDisabled(8), - isStep9Disabled: isStepDisabled(9), - isStep10Disabled: isStepDisabled(10), + isStep0Disabled: isStepDisabled("step0"), + isStep1Disabled: isStepDisabled("step1"), + isStep2Disabled: isStepDisabled("step2"), + isStep3Disabled: isStepDisabled("step3"), + isConfigureDownloadDisabled: isStepDisabled("configureDownload"), + isSelectMpacksDisabled: isStepDisabled("selectMpacks"), + isCustomMpackReposDisabled: isStepDisabled("customMpackRepos"), + isDownloadMpacksDisabled: isStepDisabled("downloadMpacks"), + isCustomProductReposDisabled: isStepDisabled("customProductRepos"), + isVerifyProductsDisabled: isStepDisabled("verifyProducts"), + isStep4Disabled: isStepDisabled("step4"), + isStep5Disabled: isStepDisabled("step5"), + isStep6Disabled: isStepDisabled("step6"), + isStep7Disabled: isStepDisabled("step7"), + isStep8Disabled: isStepDisabled("step8"), + isStep9Disabled: isStepDisabled("step9"), + isStep10Disabled: isStepDisabled("step10"), + isDownloadOptionsDisabled: isStepDisabled("downloadOptions"), + isSelectUpgradeOptionsDisabled: isStepDisabled('selectUpgradeOptions'), + isReviewConfigsDisabled: isStepDisabled('reviewConfigs'), + isSelectUpgradeTypeDisabled: isStepDisabled('selectUpgradeType'), + isUpgradeSummaryDisabled: isStepDisabled('upgradeSummary'), - isStep0Completed: isStepCompleted(0), - isStep1Completed: isStepCompleted(1), - isStep2Completed: isStepCompleted(2), - isStep3Completed: isStepCompleted(3), - isStep4Completed: isStepCompleted(4), - isStep5Completed: isStepCompleted(5), - isStep6Completed: isStepCompleted(6), - isStep7Completed: isStepCompleted(7), - isStep8Completed: isStepCompleted(8), - isStep9Completed: isStepCompleted(9), - isStep10Completed: isStepCompleted(10) + isStep0Completed: isStepCompleted("step0"), + isStep1Completed: isStepCompleted("step1"), + isStep2Completed: isStepCompleted("step2"), + isStep3Completed: isStepCompleted("step3"), + isConfigureDownloadCompleted: isStepCompleted("configureDownload"), + isSelectMpacksCompleted: isStepCompleted("selectMpacks"), + isCustomMpackReposCompleted: isStepCompleted("customMpackRepos"), + isDownloadMpacksCompleted: isStepCompleted("downloadMpacks"), + isCustomProductReposCompleted: isStepCompleted("customProductRepos"), + isVerifyProductsCompleted: isStepCompleted("verifyProducts"), + isStep4Completed: isStepCompleted("step4"), + isStep5Completed: isStepCompleted("step5"), + isStep6Completed: isStepCompleted("step6"), + isStep7Completed: isStepCompleted("step7"), + isStep8Completed: isStepCompleted("step8"), + isStep9Completed: isStepCompleted("step9"), + isStep10Completed: isStepCompleted("step10"), + isDownloadOptionsCompleted: isStepCompleted("downloadOptions"), + isSelectUpgradeOptionsCompleted: isStepCompleted("selectUpgradeOptions"), + isReviewConfigsCompleted: isStepCompleted("reviewConfigs"), + isSelectUpgradeTypeCompleted: isStepCompleted("selectUpgradeType"), + isUpgradeSummaryCompleted: isStepCompleted("upgradeSummary") }); diff --git a/ambari-web/app/models/repository.js b/ambari-web/app/models/repository.js index 58314cf9a9c..56e97c2865d 100644 --- a/ambari-web/app/models/repository.js +++ b/ambari-web/app/models/repository.js @@ -34,6 +34,7 @@ App.Repository = DS.Model.extend({ operatingSystem: DS.belongsTo('App.OperatingSystem'), components: DS.attr('string'), distribution: DS.attr('string'), + unique: DS.attr('boolean'), tags: DS.attr('array'), validation: DS.attr('string', {defaultValue: ''}), @@ -80,8 +81,11 @@ App.Repository = DS.Model.extend({ * @type {boolean} */ showRepo: function () { - const isGPLAccepted = App.router.get('clusterController.ambariProperties')['gpl.license.accepted'] === 'true'; - return isGPLAccepted || !this.get('isGPL'); + const ambariProperties = App.router.get('clusterController.ambariProperties'); + if (ambariProperties && ambariProperties['gpl.license.accepted'] === true) { + return true; + } + return !this.get('isGPL'); }.property('isGPL'), undo: Em.computed.notEqualProperties('baseUrl', 'baseUrlInit'), diff --git a/ambari-web/app/models/stack.js b/ambari-web/app/models/stack.js index 657ee5c1677..1fbc3297b81 100644 --- a/ambari-web/app/models/stack.js +++ b/ambari-web/app/models/stack.js @@ -34,8 +34,6 @@ App.Stack = DS.Model.extend({ operatingSystems: DS.hasMany('App.OperatingSystem'), isSelected: DS.attr('boolean', {defaultValue: false}), - versionInfoId: null, - stackNameVersion: Em.computed.concat('-', 'stackName', 'stackVersion'), isPatch: Em.computed.equal('type', 'PATCH'), diff --git a/ambari-web/app/router.js b/ambari-web/app/router.js index e104effbfc2..fc754b77a39 100644 --- a/ambari-web/app/router.js +++ b/ambari-web/app/router.js @@ -43,6 +43,29 @@ App.WizardRoute = Em.Route.extend({ gotoStep10: Em.Router.transitionTo('step10'), + gotoConfigureDownload: Em.Router.transitionTo('configureDownload'), + + gotoSelectMpacks: Em.Router.transitionTo('selectMpacks'), + + gotoCustomMpackRepos: Em.Router.transitionTo('customMpackRepos'), + + gotoDownloadMpacks: Em.Router.transitionTo('downloadMpacks'), + + gotoCustomProductRepos: Em.Router.transitionTo('customProductRepos'), + + gotoVerifyProducts: Em.Router.transitionTo('verifyProducts'), + + gotoDownloadOptions: Em.Router.transitionTo('downloadOptions'), + + gotoSelectUpgradeOptions: Em.Router.transitionTo('selectUpgradeOptions'), + + gotoReviewConfigs: Em.Router.transitionTo('reviewConfigs'), + + gotoSelectUpgradeType: Em.Router.transitionTo('selectUpgradeType'), + + gotoUpgradeSummary: Em.Router.transitionTo('upgradeSummary'), + + isRoutable: function() { return typeof this.get('route') === 'string' && App.router.get('loggedIn'); }.property('App.router.loggedIn') @@ -204,7 +227,7 @@ App.Router = Em.Router.extend({ } else { newStep = step; } - + var previousStep = parseInt(this.getInstallerCurrentStep(), 10); this.set('isFwdNavigation', newStep >= previousStep); }, diff --git a/ambari-web/app/routes/installer.js b/ambari-web/app/routes/installer.js index d1d7fc8170b..ffa392f2a34 100644 --- a/ambari-web/app/routes/installer.js +++ b/ambari-web/app/routes/installer.js @@ -91,12 +91,16 @@ module.exports = Em.Route.extend(App.RouterRedirections, { step0: Em.Route.extend({ route: '/step0', + breadcrumbs: { label: Em.I18n.translations['installer.step0.header'] }, connectOutlets: function (router) { console.time('step0 connectOutlets'); var self = this; var controller = router.get('installerController'); + var wizardStep0Controller = router.get('wizardStep0Controller'); + wizardStep0Controller.set('wizardController', controller); controller.setCurrentStep('step0'); controller.loadAllPriorSteps().done(function () { + controller.setStepsEnable(); controller.connectOutlet('wizardStep0', controller.get('content')); self.scrollTop(); console.timeEnd('step0 connectOutlets'); @@ -105,97 +109,46 @@ module.exports = Em.Route.extend(App.RouterRedirections, { next: function (router) { console.time('step0 next'); - var installerController = router.get('installerController'); - installerController.save('cluster'); + var controller = router.get('installerController'); + controller.save('cluster'); App.db.setStacks(undefined); App.db.setRepos(undefined); App.db.setLocalRepoVDFData(undefined); App.Stack.find().clear(); - installerController.set('content.stacks',undefined); + controller.set('content.stacks',undefined); router.transitionTo('step2'); console.timeEnd('step0 next'); } }), - step1: Em.Route.extend({ - route: '/step1', - connectOutlets: function (router) { - console.time('step1 connectOutlets'); - var self = this; - var controller = router.get('installerController'); - var wizardStep1Controller = router.get('wizardStep1Controller'); - wizardStep1Controller.set('skipValidationChecked', false); - wizardStep1Controller.set('optionsToSelect', { - 'usePublicRepo': { - index: 0, - isSelected: true - }, - 'useLocalRepo': { - index: 1, - isSelected: false, - 'uploadFile': { - index: 0, - name: 'uploadFile', - file: '', - hasError: false, - isSelected: true - }, - 'enterUrl': { - index: 1, - name: 'enterUrl', - url: '', - placeholder: Em.I18n.t('installer.step1.useLocalRepo.enterUrl.placeholder'), - hasError: false, - isSelected: false - } - } - }); - controller.setCurrentStep('step1'); - controller.loadAllPriorSteps().done(function () { - wizardStep1Controller.set('wizardController', controller); - controller.connectOutlet('wizardStep1', controller.get('content')); - self.scrollTop(); - console.timeEnd('step1 connectOutlets'); - }); - }, - back: Em.Router.transitionTo('step3'), - next: function (router) { - console.time('step1 next'); - if(router.get('btnClickInProgress')) { - return; - } - var wizardStep1Controller = router.get('wizardStep1Controller'); - var installerController = router.get('installerController'); - installerController.validateJDKVersion(function() { - installerController.checkRepoURL(wizardStep1Controller).done(function () { - App.set('router.nextBtnClickInProgress', true); - installerController.setDBProperty('service', undefined); - installerController.setStacks(); - router.transitionTo('step4'); - console.timeEnd('step1 next'); - }); - }, function() {}); - } - }), - step2: Em.Route.extend({ route: '/step2', + breadcrumbs: { label: Em.I18n.translations['installer.step2.header'] }, connectOutlets: function (router, context) { console.time('step2 connectOutlets'); var self = this; var controller = router.get('installerController'); + var wizardStep2Controller = router.get('wizardStep2Controller'); + wizardStep2Controller.set('wizardController', controller); var newStepIndex = controller.getStepIndex('step2'); router.setNavigationFlow(newStepIndex); - controller.clearInstallOptions(); + //controller.clearInstallOptions(); controller.setCurrentStep('step2'); controller.loadAllPriorSteps().done(function () { + controller.setStepsEnable(); self.scrollTop(); controller.connectOutlet('wizardStep2', controller.get('content')); console.timeEnd('step2 connectOutlets'); }); }, - back: Em.Router.transitionTo('step0'), + + backTransition: function (router) { + var controller = router.get('installerController'); + controller.clearErrors(); + router.transitionTo('step0'); + }, + next: function (router) { console.time('step2 next'); if (!router.get('btnClickInProgress')) { @@ -204,6 +157,7 @@ module.exports = Em.Route.extend(App.RouterRedirections, { controller.save('installOptions'); //hosts was saved to content.hosts inside wizardStep2Controller controller.save('hosts'); + controller.setStepSaved('step2'); router.transitionTo('step3'); } console.timeEnd('step2 next'); @@ -212,14 +166,16 @@ module.exports = Em.Route.extend(App.RouterRedirections, { step3: App.StepRoute.extend({ route: '/step3', + breadcrumbs: { label: Em.I18n.translations['installer.step3.header'] }, connectOutlets: function (router) { console.time('step3 connectOutlets'); var self = this; var controller = router.get('installerController'); + var wizardStep3Controller = router.get('wizardStep3Controller'); + wizardStep3Controller.set('wizardController', controller); controller.setCurrentStep('step3'); controller.loadAllPriorSteps().done(function () { - var wizardStep3Controller = router.get('wizardStep3Controller'); - wizardStep3Controller.set('wizardController', controller); + controller.setStepsEnable(); controller.connectOutlet('wizardStep3', controller.get('content')); self.scrollTop(); console.timeEnd('step3 connectOutlets'); @@ -227,6 +183,8 @@ module.exports = Em.Route.extend(App.RouterRedirections, { }, backTransition: function (router) { + var controller = router.get('installerController'); + controller.clearErrors(); router.transitionTo('step2'); }, @@ -234,15 +192,26 @@ module.exports = Em.Route.extend(App.RouterRedirections, { console.time('step3 next'); if (!router.get('btnClickInProgress')) { App.set('router.nextBtnClickInProgress', true); - var installerController = router.get('installerController'); + var controller = router.get('installerController'); var wizardStep3Controller = router.get('wizardStep3Controller'); - installerController.saveConfirmedHosts(wizardStep3Controller); - installerController.setDBProperties({ - bootStatus: true, - selectedServiceNames: undefined, - installedServiceNames: undefined - }); - router.transitionTo('step1'); + controller.saveConfirmedHosts(wizardStep3Controller); + if (!wizardStep3Controller.get('isSaved')) { + var wizardSelectMpacksController = App.router.get('wizardSelectMpacksController'); + wizardSelectMpacksController.set('wizardController', controller); + wizardSelectMpacksController.clearSelection(); + controller.set('content.selectedServices', undefined); + controller.set('content.selectedServiceNames', undefined); + controller.set('content.selectedMpacks', undefined); + controller.setDBProperties({ + bootStatus: true, + selectedServices: undefined, + selectedServiceNames: undefined, + installedServiceNames: undefined, + selectedMpack: undefined + }); + } + controller.setStepSaved('step3'); + router.transitionTo('configureDownload'); console.timeEnd('step3 next'); } }, @@ -261,18 +230,304 @@ module.exports = Em.Route.extend(App.RouterRedirections, { } }), + configureDownload: Em.Route.extend({ + route: '/configureDownload', + breadcrumbs: { label: Em.I18n.translations['installer.configureDownload.header'] }, + connectOutlets: function (router) { + console.time('configureDownload connectOutlets'); + var self = this; + var controller = router.get('installerController'); + var configureDownloadController = router.get('wizardConfigureDownloadController'); + configureDownloadController.set('wizardController', controller); + var newStepIndex = controller.getStepIndex('configureDownload'); + router.setNavigationFlow(newStepIndex); + controller.setCurrentStep('configureDownload'); + controller.loadAllPriorSteps().done(function () { + controller.setStepsEnable(); + controller.connectOutlet('wizardConfigureDownload', controller.get('content')); + self.scrollTop(); + console.timeEnd('configureDownload connectOutlets'); + }); + }, + + backTransition: function (router) { + var controller = router.get('installerController'); + controller.clearErrors(); + router.transitionTo('step3'); + }, + + next: function (router) { + console.time('configureDownload next'); + if(router.get('btnClickInProgress')) { + return; + } + App.set('router.nextBtnClickInProgress', true); + var controller = router.get('installerController'); + controller.save('downloadConfig'); + controller.setDBProperty('service', undefined); + router.transitionTo('selectMpacks'); + console.timeEnd('configureDownload next'); + } + }), + + selectMpacks: App.StepRoute.extend({ + route: '/selectMpacks', + breadcrumbs: { label: Em.I18n.translations['installer.selectMpacks.header'] }, + connectOutlets: function (router) { + console.time('selectMpacks connectOutlets'); + var self = this; + var controller = router.get('installerController'); + controller.setCurrentStep('selectMpacks'); + var wizardSelectMpacksController = router.get('wizardSelectMpacksController'); + wizardSelectMpacksController.set('wizardController', controller); + controller.loadAllPriorSteps().done(function () { + controller.setStepsEnable(); + controller.connectOutlet('wizardSelectMpacks', controller.get('content')); + self.scrollTop(); + console.timeEnd('selectMpacks connectOutlets'); + }); + }, + + backTransition: function (router) { + var controller = router.get('installerController'); + controller.clearErrors(); + router.transitionTo('configureDownload'); + }, + + next: function (router, context) { + console.time('selectMpacks next'); + if (!router.get('btnClickInProgress')) { + App.set('router.nextBtnClickInProgress', true); + var controller = router.get('installerController'); + controller.save('selectedServiceNames'); + controller.save('selectedServices'); + controller.save('selectedMpacks'); + controller.save('advancedMode'); + var wizardStep6Controller = router.get('wizardStep6Controller'); + // Clear subsequent settings if user changed service selections + if (!wizardStep6Controller.get('isSaved')) { + router.get('wizardStep5Controller').clearRecommendations(); + controller.setDBProperty('recommendations', undefined); + controller.set('content.masterComponentHosts', undefined); + controller.setDBProperty('masterComponentHosts', undefined); + controller.clearEnhancedConfigs(); + controller.setDBProperty('slaveComponentHosts', undefined); + wizardStep6Controller.set('isClientsSet', false); + } + controller.setStepSaved('selectMpacks'); + const downloadConfig = controller.get('content.downloadConfig'); + if (downloadConfig && downloadConfig.useCustomRepo) { + router.transitionTo('customMpackRepos'); + } else { + router.transitionTo('downloadMpacks'); + } + console.timeEnd('selectMpacks next'); + } + }, + }), + + customMpackRepos: App.StepRoute.extend({ + route: '/customMpackRepos', + breadcrumbs: { label: Em.I18n.translations['installer.customMpackRepos.header'] }, + connectOutlets: function (router) { + console.time('customMpackRepos connectOutlets'); + var self = this; + var controller = router.get('installerController'); + const downloadConfig = controller.get('content.downloadConfig'); + + //do not allow navigation to this step unless we are using custom repos + if (downloadConfig && !downloadConfig.useCustomRepo) { + Em.run.next(function () { + router.transitionTo('downloadMpacks'); + }); + } + + var customMpackReposController = router.get('wizardCustomMpackReposController'); + customMpackReposController.set('wizardController', controller); + var newStepIndex = controller.getStepIndex('customMpackRepos'); + router.setNavigationFlow(newStepIndex); + controller.setCurrentStep('customMpackRepos'); + controller.loadAllPriorSteps().done(function () { + controller.setStepsEnable(); + controller.connectOutlet('wizardCustomMpackRepos', controller.get('content')); + self.scrollTop(); + console.timeEnd('customMpackRepos connectOutlets'); + }); + }, + + backTransition: function (router) { + var controller = router.get('installerController'); + controller.clearErrors(); + router.transitionTo('selectMpacks'); + }, + + next: function (router) { + console.time('customMpackRepos next'); + if (!router.get('btnClickInProgress')) { + App.set('router.nextBtnClickInProgress', true); + const controller = router.get('installerController'); + controller.save('selectedMpacks'); + controller.setStepSaved('customMpackRepos'); + router.transitionTo('downloadMpacks'); + console.timeEnd('customMpackRepos next'); + } + } + }), + + downloadMpacks: App.StepRoute.extend({ + route: '/downloadMpacks', + breadcrumbs: { label: Em.I18n.translations['installer.downloadMpacks.header'] }, + connectOutlets: function (router) { + console.time('downloadMpacks connectOutlets'); + var self = this; + var controller = router.get('installerController'); + var downloadMpacksController = router.get('wizardDownloadMpacksController'); + downloadMpacksController.set('wizardController', controller); + var newStepIndex = controller.getStepIndex('downloadMpacks'); + router.setNavigationFlow(newStepIndex); + controller.setCurrentStep('downloadMpacks'); + controller.loadAllPriorSteps().done(function () { + controller.setStepsEnable(); + controller.connectOutlet('wizardDownloadMpacks', controller.get('content')); + self.scrollTop(); + console.timeEnd('downloadMpacks connectOutlets'); + }); + }, + + backTransition: function (router) { + const controller = router.get('installerController'); + controller.clearErrors(); + const downloadConfig = controller.get('content.downloadConfig'); + if (downloadConfig && downloadConfig.useCustomRepo) { + router.transitionTo('customMpackRepos'); + } else { + router.transitionTo('selectMpacks'); + } + }, + + next: function (router) { + console.time('downloadMpacks next'); + if(router.get('btnClickInProgress')) { + return; + } + App.set('router.nextBtnClickInProgress', true); + const controller = router.get('installerController'); + const downloadConfig = controller.get('content.downloadConfig'); + if (downloadConfig && downloadConfig.useCustomRepo) { + router.transitionTo('customProductRepos'); + } else { + router.transitionTo('step5'); + } + console.timeEnd('downloadMpacks next'); + } + }), + + customProductRepos: App.StepRoute.extend({ + route: '/customProductRepos', + breadcrumbs: { label: Em.I18n.translations['installer.customProductRepos.header'] }, + connectOutlets: function (router) { + console.time('customProductRepos connectOutlets'); + var self = this; + var controller = router.get('installerController'); + const downloadConfig = controller.get('content.downloadConfig'); + + //disable navigation to this step unless we are using custom repos + if (downloadConfig && !downloadConfig.useCustomRepo) { + Em.run.next(function () { + router.transitionTo('verifyProducts'); + }); + } + + var customMpackReposController = router.get('wizardCustomMpackReposController'); + customMpackReposController.set('wizardController', controller); + var newStepIndex = controller.getStepIndex('customProductRepos'); + router.setNavigationFlow(newStepIndex); + controller.setCurrentStep('customProductRepos'); + controller.loadAllPriorSteps().done(function () { + controller.setStepsEnable(); + controller.connectOutlet('wizardCustomProductRepos', controller.get('content')); + self.scrollTop(); + console.timeEnd('customProductRepos connectOutlets'); + }); + }, + + backTransition: function (router) { + var controller = router.get('installerController'); + controller.clearErrors(); + router.transitionTo('downloadMpacks'); + }, + + next: function (router) { + console.time('customProductRepos next'); + if (!router.get('btnClickInProgress')) { + App.set('router.nextBtnClickInProgress', true); + const controller = router.get('installerController'); + controller.clearErrors(); + controller.save('selectedMpacks'); + controller.setStepSaved('customProductRepos'); + router.transitionTo('verifyProducts'); + console.timeEnd('customProductRepos next'); + } + } + }), + + verifyProducts: App.StepRoute.extend({ + route: '/verifyProducts', + breadcrumbs: { label: Em.I18n.translations['installer.verifyProducts.header'] }, + connectOutlets: function (router) { + console.time('verifyProducts connectOutlets'); + var self = this; + var controller = router.get('installerController'); + var verifyProductsController = router.get('wizardVerifyProductsController'); + verifyProductsController.set('wizardController', controller); + var newStepIndex = controller.getStepIndex('verifyProducts'); + router.setNavigationFlow(newStepIndex); + controller.setCurrentStep('verifyProducts'); + controller.loadAllPriorSteps().done(function () { + controller.setStepsEnable(); + controller.connectOutlet('wizardVerifyProducts', controller.get('content')); + self.scrollTop(); + console.timeEnd('verifyProducts connectOutlets'); + }); + }, + + backTransition: function (router) { + const controller = router.get('installerController'); + controller.clearErrors(); + const downloadConfig = controller.get('content.downloadConfig'); + if (downloadConfig && downloadConfig.useCustomRepo) { + router.transitionTo('customProductRepos'); + } else { + router.transitionTo('downloadMpacks'); + } + }, + + next: function (router) { + console.time('verifyProducts next'); + if (router.get('btnClickInProgress')) { + return; + } + App.set('router.nextBtnClickInProgress', true); + const controller = router.get('installerController'); + router.transitionTo(controller.getNextStepName()); + console.timeEnd('verifyProducts next'); + } + }), + step4: App.StepRoute.extend({ route: '/step4', + breadcrumbs: { label: Em.I18n.translations['installer.step4.header'] }, connectOutlets: function (router, context) { console.time('step4 connectOutlets'); var self = this; var controller = router.get('installerController'); + var wizardStep4Controller = router.get('wizardStep4Controller'); + wizardStep4Controller.set('wizardController', controller); var newStepIndex = controller.getStepIndex('step4'); router.setNavigationFlow(newStepIndex); controller.setCurrentStep('step4'); controller.loadAllPriorSteps().done(function () { - var wizardStep4Controller = router.get('wizardStep4Controller'); - wizardStep4Controller.set('wizardController', controller); + controller.setStepsEnable(); controller.connectOutlet('wizardStep4', App.StackService.find().filterProperty('isInstallable', true)); self.scrollTop(); console.timeEnd('step4 connectOutlets'); @@ -280,6 +535,8 @@ module.exports = Em.Route.extend(App.RouterRedirections, { }, backTransition: function(router) { + var controller = router.get('installerController'); + controller.clearErrors(); router.transitionTo('step1'); }, @@ -305,41 +562,54 @@ module.exports = Em.Route.extend(App.RouterRedirections, { step5: App.StepRoute.extend({ route: '/step5', + breadcrumbs: { label: Em.I18n.translations['installer.step5.header'] }, connectOutlets: function (router, context) { console.time('step5 connectOutlets'); var self = this; var controller = router.get('installerController'); + var wizardStep5Controller = router.get('wizardStep5Controller'); + wizardStep5Controller.set('wizardController', controller); var newStepIndex = controller.getStepIndex('step5'); router.setNavigationFlow(newStepIndex); - var wizardStep5Controller = router.get('wizardStep5Controller'); wizardStep5Controller.setProperties({ servicesMasters: [], isInitialLayout: true }); controller.setCurrentStep('step5'); controller.loadAllPriorSteps().done(function () { - wizardStep5Controller.set('wizardController', controller); + controller.setStepsEnable(); controller.connectOutlet('wizardStep5', controller.get('content')); self.scrollTop(); console.timeEnd('step5 connectOutlets'); }); }, - backTransition: function(router) { - router.transitionTo('step4'); + + backTransition: function (router) { + var controller = router.get('installerController'); + controller.clearErrors(); + const downloadConfig = controller.get('content.downloadConfig'); + if (downloadConfig && downloadConfig.useCustomRepo) { + router.transitionTo('verifyProducts'); + } else { + router.transitionTo('downloadMpacks'); + } }, + next: function (router) { console.time('step5 next'); if (!router.get('btnClickInProgress')) { App.set('router.nextBtnClickInProgress', true); var controller = router.get('installerController'); var wizardStep5Controller = router.get('wizardStep5Controller'); - var wizardStep6Controller = router.get('wizardStep6Controller'); controller.saveMasterComponentHosts(wizardStep5Controller); - controller.setDBProperties({ - slaveComponentHosts: undefined, - recommendations: wizardStep5Controller.get('content.recommendations') - }); - wizardStep6Controller.set('isClientsSet', false); + controller.setDBProperty('recommendations', wizardStep5Controller.get('content.recommendations') || wizardStep5Controller.get('recommendations')); + // Clear subsequent steps if user made changes + if (!wizardStep5Controller.get('isSaved')) { + controller.setDBProperty('slaveComponentHosts', undefined); + var wizardStep6Controller = router.get('wizardStep6Controller'); + wizardStep6Controller.set('isClientsSet', false); + } + controller.setStepSaved('step5'); router.transitionTo('step6'); } console.timeEnd('step5 next'); @@ -348,21 +618,28 @@ module.exports = Em.Route.extend(App.RouterRedirections, { step6: App.StepRoute.extend({ route: '/step6', + breadcrumbs: { label: Em.I18n.translations['installer.step6.header'] }, connectOutlets: function (router, context) { console.time('step6 connectOutlets'); var self = this; var controller = router.get('installerController'); + var wizardStep6Controller = router.get('wizardStep6Controller'); + wizardStep6Controller.set('wizardController', controller); var newStepIndex = controller.getStepIndex('step6'); router.setNavigationFlow(newStepIndex); - router.get('wizardStep6Controller').set('hosts', []); controller.setCurrentStep('step6'); + wizardStep6Controller.set('hosts', []); controller.loadAllPriorSteps().done(function () { + controller.setStepsEnable(); controller.connectOutlet('wizardStep6', controller.get('content')); self.scrollTop(); console.timeEnd('step6 connectOutlets'); }); }, + backTransition: function(router) { + var controller = router.get('installerController'); + controller.clearErrors(); router.transitionTo('step5'); }, @@ -377,13 +654,17 @@ module.exports = Em.Route.extend(App.RouterRedirections, { controller.saveSlaveComponentHosts(wizardStep6Controller); controller.get('content').set('serviceConfigProperties', null); controller.get('content').set('componentsFromConfigs', []); - controller.setDBProperties({ - serviceConfigGroups: null, - recommendationsHostGroups: wizardStep6Controller.get('content.recommendationsHostGroups'), - recommendationsConfigs: null, - componentsFromConfigs: [] - }); - controller.clearServiceConfigProperties(); + // Clear subsequent steps if user made changes + if (!wizardStep6Controller.get('isSaved')) { + controller.setDBProperties({ + serviceConfigGroups: null, + recommendationsHostGroups: wizardStep6Controller.get('content.recommendationsHostGroups'), + recommendationsConfigs: null, + componentsFromConfigs: [] + }); + controller.clearServiceConfigProperties(); + } + controller.setStepSaved('step6'); router.transitionTo('step7'); console.timeEnd('step6 next'); } @@ -394,22 +675,19 @@ module.exports = Em.Route.extend(App.RouterRedirections, { step7: App.StepRoute.extend({ route: '/step7', - - enter: function (router) { - console.time('step7 enter'); - var controller = router.get('installerController'); - controller.setCurrentStep('step7'); - console.timeEnd('step7 enter'); - }, - + breadcrumbs: { label: Em.I18n.translations['installer.step7.header'] }, connectOutlets: function (router, context) { console.time('step7 connectOutlets'); var self = this; var controller = router.get('installerController'); - router.get('preInstallChecksController').loadStep(); var wizardStep7Controller = router.get('wizardStep7Controller'); + wizardStep7Controller.set('wizardController', controller); + var newStepIndex = controller.getStepIndex('step7'); + router.setNavigationFlow(newStepIndex); + controller.setCurrentStep('step7'); + router.get('preInstallChecksController').loadStep(); controller.loadAllPriorSteps().done(function () { - wizardStep7Controller.set('wizardController', controller); + controller.setStepsEnable(); controller.connectOutlet('wizardStep7', controller.get('content')); self.scrollTop(); console.timeEnd('step7 connectOutlets'); @@ -418,6 +696,9 @@ module.exports = Em.Route.extend(App.RouterRedirections, { backTransition: function (router) { console.time('step7 back'); + var controller = router.get('installerController'); + controller.clearErrors(); + var step = router.get('installerController.content.skipSlavesStep') ? 'step5' : 'step6'; var wizardStep7Controller = router.get('wizardStep7Controller'); @@ -432,6 +713,7 @@ module.exports = Em.Route.extend(App.RouterRedirections, { } console.timeEnd('step7 back'); }, + next: function (router) { console.time('step7 next'); if (!router.get('btnClickInProgress')) { @@ -455,35 +737,44 @@ module.exports = Em.Route.extend(App.RouterRedirections, { step8: App.StepRoute.extend({ route: '/step8', + breadcrumbs: { label: Em.I18n.translations['installer.step8.header'] }, connectOutlets: function (router, context) { console.time('step8 connectOutlets'); - var controller = router.get('installerController'); var self = this; + var controller = router.get('installerController'); + var wizardStep8Controller = router.get('wizardStep8Controller'); + wizardStep8Controller.set('wizardController', controller); + var newStepIndex = controller.getStepIndex('step8'); + router.setNavigationFlow(newStepIndex); controller.setCurrentStep('step8'); controller.loadAllPriorSteps().done(function () { - var wizardStep8Controller = router.get('wizardStep8Controller'); - wizardStep8Controller.set('wizardController', controller); + controller.setStepsEnable(); controller.connectOutlet('wizardStep8', controller.get('content')); self.scrollTop(); console.timeEnd('step8 connectOutlets'); }); }, + backTransition: function (router) { - if(router.get('wizardStep8Controller.isBackBtnDisabled') == false) { + if (router.get('wizardStep8Controller.isBackBtnDisabled') == false) { + var controller = router.get('installerController'); + controller.clearErrors(); + router.transitionTo('step7'); } }, + next: function (router) { console.time('step8 next'); if (!router.get('btnClickInProgress')) { App.set('router.nextBtnClickInProgress', true); - var installerController = router.get('installerController'); + var controller = router.get('installerController'); var wizardStep8Controller = router.get('wizardStep8Controller'); // invoke API call to install selected services - installerController.installServices(false, function () { - installerController.setInfoForStep9(); + controller.installServices(false, function () { + controller.setInfoForStep9(); // We need to do recovery based on whether we are in Add Host or Installer wizard - installerController.saveClusterState('CLUSTER_INSTALLING_3'); + controller.saveClusterState('CLUSTER_INSTALLING_3'); wizardStep8Controller.set('servicesInstalled', true); router.transitionTo('step9'); console.timeEnd('step8 next'); @@ -494,37 +785,47 @@ module.exports = Em.Route.extend(App.RouterRedirections, { step9: Em.Route.extend({ route: '/step9', + breadcrumbs: { label: Em.I18n.translations['installer.step9.header'] }, connectOutlets: function (router, context) { console.time('step9 connectOutlets'); var self = this; - var controller = router.get('installerController'), - wizardStep9Controller = router.get('wizardStep9Controller'); + var controller = router.get('installerController'); + var wizardStep9Controller = router.get('wizardStep9Controller'); + wizardStep9Controller.set('wizardController', controller); controller.loadAllPriorSteps().done(function () { wizardStep9Controller.loadDoServiceChecksFlag().done(function () { + var newStepIndex = controller.getStepIndex('step7'); + router.setNavigationFlow(newStepIndex); controller.setCurrentStep('step9'); + controller.setStepsEnable(); if (!App.get('testMode')) { controller.setLowerStepsDisable(9); } - wizardStep9Controller.set('wizardController', controller); controller.connectOutlet('wizardStep9', controller.get('content')); self.scrollTop(); console.timeEnd('step9 connectOutlets'); }); }); }, - back: Em.Router.transitionTo('step8'), + + backTransition: function (router) { + var controller = router.get('installerController'); + controller.clearErrors(); + router.transitionTo('step8'); + }, + retry: function (router) { console.time('step9 retry'); - var installerController = router.get('installerController'); + var controller = router.get('installerController'); var wizardStep9Controller = router.get('wizardStep9Controller'); if (wizardStep9Controller.get('showRetry')) { if (wizardStep9Controller.get('content.cluster.status') === 'INSTALL FAILED') { var isRetry = true; - installerController.installServices(isRetry, function () { - installerController.setInfoForStep9(); + controller.installServices(isRetry, function () { + controller.setInfoForStep9(); wizardStep9Controller.resetHostsForRetry(); // We need to do recovery based on whether we are in Add Host or Installer wizard - installerController.saveClusterState('CLUSTER_INSTALLING_3'); + controller.saveClusterState('CLUSTER_INSTALLING_3'); wizardStep9Controller.navigateStep(); }); } else { @@ -533,6 +834,7 @@ module.exports = Em.Route.extend(App.RouterRedirections, { console.timeEnd('step9 retry'); } }, + unroutePath: function (router, context) { // exclusion for transition to Admin view or Views view if (context === '/adminView' || @@ -542,14 +844,15 @@ module.exports = Em.Route.extend(App.RouterRedirections, { return false; } }, + next: function (router) { console.time('step9 next'); if(!router.get('btnClickInProgress')) { App.set('router.nextBtnClickInProgress', true); - var installerController = router.get('installerController'); + var controller = router.get('installerController'); var wizardStep9Controller = router.get('wizardStep9Controller'); - installerController.saveInstalledHosts(wizardStep9Controller); - installerController.saveClusterState('CLUSTER_INSTALLED_4'); + controller.saveInstalledHosts(wizardStep9Controller); + controller.saveClusterState('CLUSTER_INSTALLED_4'); router.transitionTo('step10'); console.timeEnd('step9 next'); } @@ -558,19 +861,31 @@ module.exports = Em.Route.extend(App.RouterRedirections, { step10: Em.Route.extend({ route: '/step10', + breadcrumbs: { label: Em.I18n.translations['installer.step10.header'] }, connectOutlets: function (router, context) { var self = this; var controller = router.get('installerController'); + var wizardStep10Controller = router.get('wizardStep10Controller'); + wizardStep10Controller.set('wizardController', controller); controller.loadAllPriorSteps().done(function () { if (!App.get('testMode')) { + var newStepIndex = controller.getStepIndex('step10'); + router.setNavigationFlow(newStepIndex); controller.setCurrentStep('step10'); + controller.setStepsEnable(); controller.setLowerStepsDisable(10); } controller.connectOutlet('wizardStep10', controller.get('content')); self.scrollTop(); }); }, - back: Em.Router.transitionTo('step9'), + + backTransition: function (router) { + var controller = router.get('installerController'); + controller.clearErrors(); + router.transitionTo('step9'); + }, + complete: function (router, context) { var controller = router.get('installerController'); controller.finish(); @@ -604,6 +919,18 @@ module.exports = Em.Route.extend(App.RouterRedirections, { gotoStep9: Em.Router.transitionTo('step9'), - gotoStep10: Em.Router.transitionTo('step10') + gotoStep10: Em.Router.transitionTo('step10'), + + gotoConfigureDownload: Em.Router.transitionTo('configureDownload'), + + gotoSelectMpacks: Em.Router.transitionTo('selectMpacks'), + + gotoCustomMpackRepos: Em.Router.transitionTo('customMpackRepos'), + + gotoDownloadMpacks: Em.Router.transitionTo('downloadMpacks'), + + gotoCustomProductRepos: Em.Router.transitionTo('customProductRepos'), + + gotoVerifyProducts: Em.Router.transitionTo('verifyProducts') }); diff --git a/ambari-web/app/routes/main.js b/ambari-web/app/routes/main.js index 194fb14a562..20074cec096 100644 --- a/ambari-web/app/routes/main.js +++ b/ambari-web/app/routes/main.js @@ -562,6 +562,26 @@ module.exports = Em.Route.extend(App.RouterRedirections, { }) }), + serviceGroups: Em.Route.extend({ + breadcrumbs: { + label: Em.I18n.t('admin.serviceGroups.title') + }, + + route: '/serviceGroups', + + enter: function (router, transition) { + if (router.get('loggedIn') && !App.isAuthorized('CLUSTER.VIEW_STACK_DETAILS')) { + router.transitionTo('main.dashboard.index'); + } + }, + + connectOutlets: function (router) { + router.set('mainAdminController.category', "adminServiceGroups"); + router.set('mainAdminController.categoryLabel', Em.I18n.t('common.serviceGroups')); + router.get('mainAdminController').connectOutlet('mainAdminServiceGroups'); + } + }), + stackAndUpgrade: Em.Route.extend({ route: '/stack', breadcrumbs: null, @@ -617,6 +637,7 @@ module.exports = Em.Route.extend(App.RouterRedirections, { router.transitionTo(event.context); } }), + stackUpgrade: require('routes/stack_upgrade_routes'), adminAdvanced: Em.Route.extend({ @@ -696,7 +717,9 @@ module.exports = Em.Route.extend(App.RouterRedirections, { if(!isDisabled){ router.transitionTo(event.context.url); } - } + }, + + createUpgradePlan: require('routes/mpack_upgrade_routes') }), diff --git a/ambari-web/app/routes/mpack_upgrade_routes.js b/ambari-web/app/routes/mpack_upgrade_routes.js new file mode 100644 index 00000000000..ad55e3cf34b --- /dev/null +++ b/ambari-web/app/routes/mpack_upgrade_routes.js @@ -0,0 +1,225 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +module.exports = App.WizardRoute.extend(App.RouterRedirections,{ + route: '/createUpgradePlan', + + breadcrumbs: { + label: Em.I18n.t('admin.createUpgradePlan.wizard.header') + }, + + enter: function (router) { + var createUpgradePlanWizardController = router.get('createUpgradePlanWizardController'); + createUpgradePlanWizardController.dataLoading().done(function () { + App.router.get('updateController').set('isWorking', false); + var popup = App.ModalPopup.show({ + classNames: ['wizard-modal-wrapper'], + modalDialogClasses: ['modal-xlg'], + header: Em.I18n.t('admin.createUpgradePlan.wizard.header'), + bodyClass: App.CreateUpgradePlanWizardView.extend({ + controller: createUpgradePlanWizardController + }), + primary: Em.I18n.t('form.cancel'), + showFooter: false, + secondary: null, + //construct cases where close button needs to be hidden. Make it observable on wizard steps + //default --- this.set('showCloseButton', true); + hideCloseButton: function () { + this.set('showCloseButton', true); + }, + //call any cleanup functions here before resetOnClose + onClose: function () { + var controller = App.router.get('createUpgradePlanWizardController'); + controller.resetOnClose(controller, 'main.admin.serviceGroups'); + }, + didInsertElement: function () { + this._super(); + this.fitHeight(); + } + }); + createUpgradePlanWizardController.set('popup', popup); + var currentClusterStatus = App.clusterStatus.get('value'); + if (currentClusterStatus) { + switch (currentClusterStatus.clusterState) { + case 'CREATE_UPGRADE_PLAN' : + createUpgradePlanWizardController.setCurrentStep(currentClusterStatus.localdb.CreateUpgradePlanWizard.currentStep); + break; + default: + var currStep = App.router.get('createUpgradePlanWizardController.currentStep'); + createUpgradePlanWizardController.setCurrentStep(currStep); + break; + } + } + Em.run.next(function () { + App.router.get('wizardWatcherController').setUser(createUpgradePlanWizardController.get('name')); + router.transitionTo(createUpgradePlanWizardController.get('currentStepName')); + }); + }); + }, + + downloadOptions: App.StepRoute.extend({ + route: '/downloadOptions', + connectOutlets: function (router) { + var controller = router.get('createUpgradePlanWizardController'), + createUpgradePlanWizardDownloadOptionsController = router.get('createUpgradePlanWizardDownloadOptionsController'); + controller.dataLoading().done(function () { + controller.setCurrentStep('downloadOptions'); + controller.loadAllPriorSteps().done(function () { + controller.connectOutlet('createUpgradePlanWizardDownloadOptions', controller.get('content')); + }); + }) + }, + unroutePath: function () { + return false; + }, + //call any functions that load data required for the next step + next: function (router) { + var controller = router.get('createUpgradePlanWizardController'); + router.transitionTo('selectUpgradeOptions'); + } + }), + + selectUpgradeOptions: App.StepRoute.extend({ + route: '/selectUpgradeOptions', + connectOutlets: function (router) { + var controller = router.get('createUpgradePlanWizardController'), + createUpgradePlanWizardselectUpgradeOptionsController = router.get('createUpgradePlanWizardselectUpgradeOptionsController'); + controller.dataLoading().done(function () { + controller.setCurrentStep('selectUpgradeOptions'); + controller.loadAllPriorSteps().done(function () { + controller.connectOutlet('createUpgradePlanWizardSelectUpgradeOptions', controller.get('content')); + }); + }) + }, + unroutePath: function () { + return false; + }, + //call any functions that load data required for the next step + back: function (router) { + var controller = router.get('createUpgradePlanWizardController'); + router.transitionTo('downloadOptions'); + }, + next: function (router) { + var controller = router.get('createUpgradePlanWizardController'); + router.transitionTo('downloadMpacks'); + } + }), + + downloadMpacks: App.StepRoute.extend({ + route: '/downloadMpacks', + connectOutlets: function (router) { + var controller = router.get('createUpgradePlanWizardController'), + createUpgradePlanWizardDownloadMpacksController = router.get('createUpgradePlanWizardDownloadMpacksController'); + controller.dataLoading().done(function () { + controller.setCurrentStep('downloadMpacks'); + controller.loadAllPriorSteps().done(function () { + controller.connectOutlet('createUpgradePlanWizardDownloadMpacks', controller.get('content')); + }); + }) + }, + unroutePath: function () { + return false; + }, + back: function (router) { + var controller = router.get('createUpgradePlanWizardController'); + router.transitionTo('selectUpgradeOptions'); + }, + //call any functions that load data required for the next step + next: function (router) { + var controller = router.get('createUpgradePlanWizardController'); + router.transitionTo('reviewConfigs'); + } + }), + + reviewConfigs: App.StepRoute.extend({ + route: '/reviewConfigs', + connectOutlets: function (router) { + var controller = router.get('createUpgradePlanWizardController'), + createUpgradePlanWizardReviewConfigsController = router.get('createUpgradePlanWizardReviewConfigsController'); + controller.dataLoading().done(function () { + controller.setCurrentStep('reviewConfigs'); + controller.loadAllPriorSteps().done(function () { + controller.connectOutlet('createUpgradePlanWizardReviewConfigs', controller.get('content')); + }); + }) + }, + unroutePath: function () { + return false; + }, + back: function (router) { + var controller = router.get('createUpgradePlanWizardController'); + router.transitionTo('downloadMpacks'); + }, + //call any functions that load data required for the next step + next: function (router) { + var controller = router.get('createUpgradePlanWizardController'); + router.transitionTo('selectUpgradeType'); + } + }), + + selectUpgradeType: App.StepRoute.extend({ + route: '/selectUpgradeType', + connectOutlets: function (router) { + var controller = router.get('createUpgradePlanWizardController'), + createUpgradePlanWizardselectUpgradeTypeController = router.get('createUpgradePlanWizardSelectUpgradeTypeController'); + controller.dataLoading().done(function () { + controller.setCurrentStep('selectUpgradeType'); + controller.loadAllPriorSteps().done(function () { + controller.connectOutlet('createUpgradePlanWizardSelectUpgradeType', controller.get('content')); + }); + }) + }, + unroutePath: function () { + return false; + }, + back: function (router) { + var controller = router.get('createUpgradePlanWizardController'); + router.transitionTo('reviewConfigs'); + }, + //call any functions that load data required for the next step + next: function (router) { + var controller = router.get('createUpgradePlanWizardController'); + router.transitionTo('upgradeSummary'); + } + }), + + upgradeSummary: App.StepRoute.extend({ + route: '/upgradeSummary', + connectOutlets: function (router) { + var controller = router.get('createUpgradePlanWizardController'), + createUpgradePlanWizardUpgradeSummaryController = router.get('createUpgradePlanWizardUpgradeSummaryController'); + controller.dataLoading().done(function () { + controller.setCurrentStep('upgradeSummary'); + controller.loadAllPriorSteps().done(function () { + controller.connectOutlet('createUpgradePlanWizardUpgradeSummary', controller.get('content')); + }); + }) + }, + unroutePath: function () { + return false; + }, + //call any functions that load data required for the next step + complete: function (router, context) { + var controller = router.get('createUpgradePlanWizardController'); + controller.resetOnClose(controller, 'main.admin.serviceGroups'); + } + }), + +}); diff --git a/ambari-web/app/styles/application.less b/ambari-web/app/styles/application.less index a6a756b2b23..51f9dc673a1 100644 --- a/ambari-web/app/styles/application.less +++ b/ambari-web/app/styles/application.less @@ -26,7 +26,7 @@ body { html, body { height: 100%; - background-color: #f0f0f0; + background-color: #fafafa; } .form-text { @@ -1110,7 +1110,7 @@ a:focus { } .active { .glyphicon-blackboard { - color: #f0f0f0; + color: #eee; } } @@ -2554,11 +2554,317 @@ a.abort-icon:hover { color: #D2D3D5; } +.repo-selected { + border: 2px solid #32BEBC; +} + +.repo-not-selected { + opacity: 0.6; +} + +.capsule { + height: 22px; + width: auto; + padding-left: 10px; + padding-right: 10px; + margin-bottom: 5px; + background-color: #ddd; + font-size: 10px; + border-radius: 12px; + border: none; + outline: none; + white-space: nowrap; + + &:hover { + background-color: #ccc; + } + + .icon { + color: #999; + font-style: normal; + } +} + +.display-flex { + display: flex !important; + display: -webkit-flex !important; + display: -moz-flex !important; + display: -ms-flex !important; + display: -o-flex !important; + &.direction-row { + flex-direction: row; + } + &.direction-col { + flex-direction: column; + } + &.align-center { + align-items: center; + } + &.justify-center { + justify-content: center; + } + + .flex-fill { + flex: auto; + } +} + + +.no-data { + position: absolute; + color: #ccc; + width: 100%; + text-align: center; + top: 50%; + transform: translateY(-50%); + :before { + content: '\f114'; + font-family: 'FontAwesome'; + display: block; + font-size: 96px; + color: #ccc; + line-height: 1em; + } +} + +td .no-data { + position: initial; + transform: none; + margin: 15px; +} + +.row.same-col-heights { + display: table; +} +.row.same-col-heights [class*="col-"]{ + &.wizard-nav, + &.wizard-content { + display: table-cell; + float: none; + } + &.wizard-content { + width: 9999999999px; + } +} + +.input-group-btn { + .icon-button-addon { + padding: 0 10px; + } +} + +.icon-button { + border: none; + outline: none; + background-color: inherit; + padding: 0; + margin-left: 5px; + margin-right: 5px; + color: #666; + + &:after { + font-family: 'FontAwesome'; + font-size: 14px; + } + + &:hover { + color: #1491c1; + } + + &:active { + color: #fff; + text-shadow: 0 0 2px #1491c1; + } +} + +[disabled].icon-button { + color: #ccc; +} + +[data-toggle="tooltip"] [disabled] { + pointer-events: none; +} + +.retry-button:after { + content: '\f01e'; +} + +.viewLog-button:after { + content: '\f0f6'; +} + +.more-info { + padding-left: 5px; + cursor: help; + &:after { + content: '\f059'; + font-family: 'FontAwesome'; + color: #666; + } +} + +.doc-icon { + padding-left: 5px; + &:after { + content: '\f02d'; + font-family: 'FontAwesome'; + color: #666; + } +} + +.tooltip { + font-family: 'Roboto'; + font-size: 10px; +} + +.tooltip-inner { + border-radius: 2px; + background-color: #666; + padding: 5px 10px; +} + +.tooltip.top .tooltip-arrow { + border-top-color: #666; +} + +.tooltip.bottom .tooltip-arrow { + border-bottom-color: #666; +} + +.tooltip.left .tooltip-arrow { + border-left-color: #666; +} + +.tooltip.right .tooltip-arrow { + border-right-color: #666; +} + +.required:after { + content: ' *'; + color: #ef6162; + padding-left: 5px; + position: absolute; +} + .container-wrap-table { padding: 0 10px; background-color: @diff-background-equal; } +#admin-service-groups { + div.timeline { + height: 190px; + width: 100%; + overflow: auto; + + ol { + display: flex; + height: 170px; + + li.timeline-event { + list-style: none; + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-evenly; + position: relative; + flex-shrink: 0; + width: 120px; + height: 60px; + padding: 5px; + color: #999; + background-color: #eee; + border-radius: 4px; + + &:not(:last-of-type):before { + height: 2px; + width: 100%; + position: absolute; + left: 50%; + content: " "; + background-color: #ccc; + } + + &:nth-of-type(odd):before { + bottom: ~"calc(-50% + 4px)"; + } + + &:nth-of-type(even):before { + top: ~"calc(-50% + 4px)"; + } + + &:after { + height: 14px; + width: 14px; + border-radius: 50%; + content: " "; + position: absolute; + background-color: #666; + left: 50%; + transform: translateX(-50%); + border: white 1px solid; + } + + &:nth-of-type(odd) { + align-self: flex-start; + + &:after { + bottom: ~"calc(-50% - 2px)"; + } + } + + &:nth-of-type(even) { + align-self: flex-end; + + &:after { + top: ~"calc(-50% - 2px)"; + } + } + + &:last-of-type { + color: #ccc; + background-color: #1eb475; + + .event-heading { + color: #fafafa; + } + + &:after { + width: 13px; + height: 13px; + background-color: inherit; + border: none; + box-shadow: 0 0 0 8px #1eb47599; + } + } + + .event-heading { + color: #666; + font-size: 16px; + + .icon { + font-family: 'FontAwesome'; + + &.release { + display: none; + } + + &.patch:before { + color: #e98a40; + content: '\f0fa'; + } + + &.hotfix:before { + color: #ef6162; + content: '\f188'; + } + } + } + } + } + } +} + .bulk-host-display { margin-bottom: 10px; pre { @@ -2601,4 +2907,163 @@ a.abort-icon:hover { #notifications-dropdown.dropdown-menu .notifications-header .notifications-title { line-height: 30px; +} + +#start-upgrade-plan { + text-align: center; + padding: 30px 0; + max-width: 1000px; + + h3 { + margin-top: 0; + color: #333; + } + + #description { + width: 60%; + margin-left: auto; + margin-right: auto; + color: #999; + } +} + +.upgrade-steps { + display: flex; + justify-content: space-around; + margin: 40px auto; + counter-reset: items; + + li { + list-style-type: none; + width: 225px; + margin: 20px 15px; + margin-bottom: 0; + color: #999; + text-align: center; + border-color: #999; + + h4 { + color: inherit; + } + + &:before { + display: block; + position: relative; + left: 50%; + transform: translateX(-50%); + bottom: 20px; + height: 30px; + width: 30px; + counter-increment: items; + content: counter(items); + border-color: inherit; + border-width: 1px; + border-style: solid; + border-radius: 100%; + font-size: 20px; + line-height: 28px; + } + + &:not(:last-child):after { + width: ~"calc(100% + 1px)"; + height: 1px; + background-color: #999; + content: ""; + position: relative; + display: block; + bottom: 138px; + left: 123px; + } + + &.active { + color: #3fae2a; + border-color: #3fae2a; + + &:before { + color: #fff; + background-color: #3fae2a; + } + } + + &.complete { + color: #3fae2a; + border-color: #3fae2a; + + h4 { + color: #333; + } + + &:before { + color: inherit; + font-family: "Glyphicons Halflings"; + content: "\e013"; + font-size: 18px; + } + + &:after { + background-color: #3fae2a; + } + } + } +} + +#upgrade-status { + .upgrade-steps { + margin: 0 -45px; + padding: 0; + + li { + margin: 0 15px; + + h4 { + margin-bottom: 0; + } + + &:before { + bottom: 0; + } + + &:not(:last-child):after { + bottom: 44px; + left: 127px; + } + } + } +} + +#upgrade-status-header { + display: flex; + align-items: center; + + button { + width: 200px; + } +} + +#upgrade-status-body { + margin: 0 -15px; + display: flex; + + > * { + //width: ~"calc(50% - 32px)"; + margin: 0 15px; + //float: left; + } + + #plan-history { + flex: 2 1 60%; + } + + #plan-mpacks { + flex-grow: 1 1 40%; + } + + table { + background: inherit; + } +} + +.btn-lg { + text-transform: uppercase; + outline: none; } \ No newline at end of file diff --git a/ambari-web/app/styles/bootstrap_overrides.less b/ambari-web/app/styles/bootstrap_overrides.less index 1d40755474f..ac5d85bf009 100644 --- a/ambari-web/app/styles/bootstrap_overrides.less +++ b/ambari-web/app/styles/bootstrap_overrides.less @@ -105,14 +105,23 @@ select.form-control { .panel { background-color: #fff; + border-radius: 0; .panel-heading { - background-color: #fff; + padding: 0; + margin-bottom: 15px; .panel-title { line-height: 34px; } } } +.panel .panel-body { + padding: 0; + position: relative; + min-height: 200px; + height: 100%; +} + .row { padding-top: 5px; padding-bottom: 5px; @@ -442,9 +451,143 @@ select.form-control { } } +.table.table-hover .action { + padding-right: 10px; +} + +.nav.nav-tabs { + margin-bottom: 15px; +} +.panel-heading .nav-tabs { + margin-bottom: 0; +} +.panel-heading .nav.nav-tabs li a { + padding-top: 0; +} +.nav.nav-tabs li.active a { + padding-bottom: 10px; +} +.wizard { + border: none; +} .wizard .wizard-body { - padding: 0 !important; + overflow: initial; + position: relative; + width: 100%; + padding: 0; + padding-bottom: 64px; // height of wizard-footer + background-color: #eee; +} +.wizard .wizard-body .wizard-content { + padding: 30px; + margin-bottom: 0; + background: #eee; +} +.wizard .wizard-body .wizard-content .step-header { + font-family: inherit; + font-weight: inherit; + color: inherit; + font-size: inherit; + font-style: inherit; + line-height: inherit; + margin-bottom: 0; +} +.wizard .wizard-body .wizard-content .step-title { + font-size: 20px; + margin: 0; + margin-bottom: 20px; +} +.wizard .wizard-body .wizard-content .step-description { + font-size: 14px; + color: #666; + line-height: inherit; +} +.wizard .wizard-body .wizard-content .panel.panel-default { + margin-top: 0; + padding: 30px; +} +.wizard .wizard-body .wizard-content .panel.panel-default .panel-body { + padding: 0; +} +.wizard .wizard-body .wizard-content .panel { + margin-bottom: 0; +} +.panel-default > .panel-heading { + border: none; + background-color: inherit; +} +.wizard .wizard-body .wizard-nav { + min-width: 250px; + padding: 30px; + background-color: #333; + margin-bottom: 0; + padding-bottom: 0; +} +.wizard .wizard-body .wizard-nav .nav li { + padding: 0; + margin-bottom: 30px; +} +.wizard .wizard-body .wizard-nav .nav li:last-child { + margin-bottom: 30px; +} +.wizard .wizard-body .wizard-nav .nav li a { + height: auto; + display: block; + vertical-align: initial; + min-height: 30px; + padding: 0; +} +.wizard .wizard-body .wizard-nav .nav li .step-marker { + top: auto; + width: 30px; + height: 30px; + color: #3fae2a; + border: 2px solid #3fae2a; + font-size: 16px; + background-color: #333; +} +.wizard .wizard-body .wizard-nav .nav li .step-name { + line-height: 1.3; + font-size: 16px; + margin-left: 40px; + position: absolute; + top: 50%; + transform: translateY(-50%); +} +.wizard .wizard-body .wizard-nav .nav li .step-index { + line-height: inherit; + position: absolute; + top: calc(50% + 1px); + transform: translateY(-50%); + left: 0; + right: 0; +} +.wizard .wizard-body .wizard-nav .nav li.completed .step-marker:after { + position: absolute; + top: calc(50% + 1px); + transform: translateY(-50%); + left: 1px; + right: 0; + font-family: "FontAwesome"; + font-size: 16px; + content: "\f00c"; +} +.wizard .wizard-body .wizard-nav .nav li.completed:after { + background-color: #3fae2a; + top: 30px; + left: 14px; +} +.wizard .wizard-body .wizard-nav .nav li.disabled.completed .step-marker { + background-color: #3fae2a; + border: 2px solid #3fae2a; +} +.wizard .wizard-body .wizard-footer { + padding: 15px 20px; + margin: 0; + position: absolute; + bottom: 0; + left: 0; } .table.table-hover .action { diff --git a/ambari-web/app/styles/common.less b/ambari-web/app/styles/common.less index b666ab3a693..3703633a01a 100644 --- a/ambari-web/app/styles/common.less +++ b/ambari-web/app/styles/common.less @@ -23,14 +23,14 @@ /************************************************************************ * Health status(service/host/host component health)icons class names ***********************************************************************/ -@health-status-red-icon: glyphicon-warning-sign; -@health-status-red-icon: icon-warning-sign; +@health-status-red-icon: glyphicon-remove-sign; +@health-status-red-icon: icon-remove-sign; @health-status-green-icon: glyphicon-ok-sign; @health-status-green-icon: icon-ok-sign; @health-status-yellow-icon: glyphicon-question-sign; @health-status-yellow-icon: icon-question-sign; -@health-status-orange-icon: glyphicon-minus-sign; -@health-status-orange-icon: icon-minus-sign; +@health-status-orange-icon: glyphicon-warning-sign; +@health-status-orange-icon: icon-warning-sign; @maintenance-icon: icon-medkit; /************************************************************************ * Health status(service/host/host component health)icon colors diff --git a/ambari-web/app/styles/dashrow.less b/ambari-web/app/styles/dashrow.less new file mode 100644 index 00000000000..4d7c064b6b2 --- /dev/null +++ b/ambari-web/app/styles/dashrow.less @@ -0,0 +1,98 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import 'common.less'; + +.dashrow { + margin-bottom: 20px; + border-radius: 4px; + box-shadow: 0 0 10px 2px rgba(0,0,0,0.1); + + .panel { + border-radius: inherit; + + .panel-heading { + height: 90px; + margin: 0; + padding: 15px 30px; + border-radius: inherit; + display: flex; + align-items: center; + font-size: 20px; + + .panel-title { + color: #333; + margin-right: 15px; + font-size: inherit; + } + + .dropdown { + margin-left: auto; + margin-right: 15px; + + .menu-button { + border: none; + outline: none; + + &:after { + content: '\e235'; + font-family: 'Glyphicons Halflings'; + vertical-align: text-bottom; + font-size: inherit; + } + } + + & ~ .collapse-button { + margin-left: initial; + } + } + + .collapse-button { + width: 20px; + padding: 0; + border: none; + outline: none; + margin-left: auto; + + &:after { + content: '\f078'; + font-family: 'FontAwesome'; + vertical-align: text-bottom; + font-size: inherit; + } + + &.collapsed:after { + content: '\f054'; + } + } + } + + .panel-collapse { + border-bottom-left-radius: inherit; + border-bottom-right-radius: inherit; + background: #fafafa; + + .panel-body { + padding: 15px 30px; + } + } + } +} + +.open .dropdown-menu { + display: block; +} \ No newline at end of file diff --git a/ambari-web/app/styles/modal_popups.less b/ambari-web/app/styles/modal_popups.less index b26bf1a42dc..a02abcab74b 100644 --- a/ambari-web/app/styles/modal_popups.less +++ b/ambari-web/app/styles/modal_popups.less @@ -99,6 +99,18 @@ } } } + + &.wide-modal { + right: initial; + left: 50%; + transform: translateX(-50%); + } + + &.no-header { + .modal-header { + display: none; + } + } } .modal-body { max-height: 600px; diff --git a/ambari-web/app/styles/stack_versions.less b/ambari-web/app/styles/stack_versions.less index f47e3fc7511..4a98e0fd86a 100644 --- a/ambari-web/app/styles/stack_versions.less +++ b/ambari-web/app/styles/stack_versions.less @@ -19,7 +19,7 @@ #advancedRepoAccordion{ .panel-heading { - background-color: #f0f0f0; + background-color: #eee; a:hover { text-decoration: none; } diff --git a/ambari-web/app/styles/theme/bootstrap-ambari.css b/ambari-web/app/styles/theme/bootstrap-ambari.css index 3da410e6a62..1e363a9b2fb 100644 --- a/ambari-web/app/styles/theme/bootstrap-ambari.css +++ b/ambari-web/app/styles/theme/bootstrap-ambari.css @@ -308,6 +308,15 @@ .dropdown .btn.dropdown-toggle[disabled] { opacity: 0.6; } +.dropdown-menu > li > div { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.42857143; + color: #333; + white-space: nowrap; +} input.form-control { font-size: 14px; border-radius: 2px; @@ -401,6 +410,10 @@ h2.table-title { top: 4px; margin-bottom: 0; } +.table .dropdown input[type="checkbox"] + label { + font-size: inherit; + top: unset; +} .table thead > tr > th { border-bottom-color: #EEE; } @@ -592,6 +605,18 @@ h2.table-title { margin-bottom: -99999px; padding-bottom: 99999px; } + +.wizard .wizard-body .wizard-nav ol.nav { + list-style: none; + margin: 0; + padding: 0; + counter-reset: items; +} +.wizard .wizard-body .wizard-nav ol.nav li .step-index:before { + counter-increment: items; + content: counter(items); +} + .wizard .wizard-body .wizard-nav .nav li { padding: 0px 15px; } @@ -646,7 +671,8 @@ h2.table-title { padding-left: 2px; } .wizard .wizard-body .wizard-nav .nav li.completed .step-marker .step-index { - display: none; + visibility: hidden; + position: absolute; } .wizard .wizard-body .wizard-nav .nav li.completed .step-marker:after { font-family: "Glyphicons Halflings"; diff --git a/ambari-web/app/styles/widgets.less b/ambari-web/app/styles/widgets.less index 3f4a7075705..9a8e4c24ff3 100644 --- a/ambari-web/app/styles/widgets.less +++ b/ambari-web/app/styles/widgets.less @@ -496,6 +496,4 @@ .compare-mode { background-color: rgba(211, 237, 247, 0.39); padding: 10px 5px 0 10px; -} - - +} \ No newline at end of file diff --git a/ambari-web/app/styles/wizard.less b/ambari-web/app/styles/wizard.less index 67fc5f8481e..b44d96dd64d 100644 --- a/ambari-web/app/styles/wizard.less +++ b/ambari-web/app/styles/wizard.less @@ -47,11 +47,9 @@ border: 1px solid #ddd; } .panel.panel-default.panel-internal.install-retry-panel { - border-top: none; - border-left: none; - border-right: none; + border: none; } - padding: 25px; + padding: 30px; background-color: #fff; } @@ -66,22 +64,117 @@ } #installOptions { - #targetHosts { - .step-title { - margin-bottom: 10px; - } - } .radio-button-options { - margin-top: 10px; - margin-bottom: 10px; + padding-bottom: 10px; + color: #666; + .radio.big-radio { + height: 100px; + background-color: #dddddd; + width: 115px; + margin: 15px; + padding-left: 0; + padding-right: 0; + border-radius: 4px; + float: left; + input[type="radio"] + label, p, .repo-group, { + cursor: default; + } + input[type="radio"] + label:before { + border-color: #999; + background-color: #fff; + } + input[type="radio"]:checked + label:before { + background: #fff; + border-color: #3FAE2A; + } + input[type="radio"]:checked + label:after { + background-color: #3FAE2A; + } + .repo-checkbox { + padding: 0 5px; + margin-top: 5px; + } + .icon { + font-size: 37px; + text-align: center; + position: absolute; + top: 15px; + padding: 0 20px; + width: 100%; + color: #fff; + } + .repo-group { + bottom: 0px; + position: absolute; + background-color: #3FAE2A; + color: white; + width: 100%; + font-size: 12px; + text-align: center; + border-radius: 2px; + } + } } .wizard-plain-text { - color: #666; + font-weight: normal; } .ssh-user, .ssh-port { padding-top: 8px; } + #host-names, #parsed-host-names { + font-size: 12px; + } + + #parsed-host-names { + background-color: #eee; + } + + .hosts-filter-box { + padding-right: 5px; + .form-control:after { + content: '\f080'; + } + } + + .hosts-table-options { + overflow: auto; + margin-bottom: 20px; + .icon-undo { + color: #666; + } + .host-count { + color: #666; + font-size: 12px; + padding-top: 10px; + } + } + + .no-pre-registered{ + color: #999; + font-size: 10px; + p { + margin-left: 30px; + } + .no-host-header { + font-size: 30px; + margin: 20px 0px 0px 30px; + } + .no-host-title { + font-size: 12px; + } + } + + .pre-registered{ + a { + color: #666; + text-decoration: none; + i { + color: #999; + } + } + } + #sshKey { color: #555; font-family: "Courier New","courier"; @@ -106,6 +199,12 @@ color: #666; } } + .icon-ellipsis-vertical { + margin-top: auto; + margin-bottom: auto; + color: #999; + font-size: 18px; + } } #confirm-hosts { @@ -137,45 +236,18 @@ } #host-filter { - - ul { - margin: 3px; - font-size: 12px; - li.filter-status.active a { - text-decoration: none; - color: #fff; - } - li.filter-status.active, - li.filter-status.active:hover { - background-color: #666; - box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); - border-radius: 5px; - } - li.filter-status:hover { - background-color: #d8d8d8; - border-radius: 5px; - } - li.divider { - color: #666; - padding: 4px 2px; - } - li { - list-style: none; - display: block; - float: left; - padding: 4px; - a { - text-decoration: underline; - } - } - li.first { - font-weight: bold; - } + padding-top: 5px; + .nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus { + background-color: transparent; } } .progress { margin-bottom: 0; } + .pre-scrollable { + float: left; + max-height: 440px; + } #confirm-hosts-table { th:first-of-type { width: 25px; @@ -206,6 +278,17 @@ #display-action { visibility:visible; } + .step3-table-progress { + .progress { + margin: 0; + width: 75%; + } + } + .step3-table-action { + .icon { + color: #999; + } + } .step3-table-checkbox { label { top: 1px; @@ -289,6 +372,16 @@ } } #deploy { + #overall-progress { + .progress { + margin-bottom: 0; + } + + .progress-message { + vertical-align: middle; + } + } + .page-bar { border: none; } @@ -607,7 +700,6 @@ } } } - max-height: 440px; } .log_popup { @@ -633,6 +725,99 @@ max-height: 440px; } +#select-mpacks { + .selected-list { + background-color: #fafafa; + } + + .capsule { + background-color: #ddd; + margin-bottom: 5px; + outline: none; + &:hover { + background-color: #ccc; + } + } + + #registry { + .option { + padding-right: 60px; + } + } + + .option { + + .option-title { + margin-right: 15px; + + select { + outline: none; + border: none; + background-color: inherit; + color: #1491c1; + margin-left: -8px; + cursor: pointer; + } + } + + .option-description { + color: #666; + } + + position: relative; + padding: 15px; + border-bottom: 1px solid; + border-color: #ddd; + &:hover { + background-color: #eee; + } + .option-add-button { + position: absolute; + top: 50%; + transform: translateY(-50%); + right: 20px; + height: 30px; + width: 30px; + padding: 0; + border-style: solid; + border-radius: 50%; + outline: none; + &.checked:after { + content: '\f00c'; + } + &:after { + position: absolute; + top: calc(~"50% + 1px"); + transform: translateY(-50%); + left: -1px; + right: 0; + content: '\f067'; + font-family: 'FontAwesome'; + } + } + .option-remove-button { + position: absolute; + top: 15px; + right: 15px; + outline: none; + border: none; + padding: 0; + background-color: inherit; + &:hover { + &:after { + color: #666; + } + } + &:after { + font-family: 'FontAwesome'; + content: '\f00d'; + color: #999; + + } + } + } +} + #select-stack .stack-version-selection{ .select-version-label { padding: 5px 25px; @@ -669,7 +854,7 @@ } } -#select-stack { +#configure-download { .stacks-options { margin-bottom: 30px; } @@ -690,38 +875,161 @@ } } } - .repos-panel { - .version-contents-body { - padding: 0px 10px; - .radio-group { - padding-bottom: 10px; - color: #666; + + .radio-group { + padding-bottom: 10px; + color: #666; + .big-radio { + display: inline-block; + height: 134px; + background-color: #ddd; + margin: 15px; + padding-left: 0; + padding-right: 0; + border-radius: 4px; + position: relative; + cursor: default; + .description { + visibility: hidden; + z-index: 999; + //border: 2px solid #1491c1; + border-radius: 4px; + background-color: #dddd; + top: 0; + bottom: 0; + left: 0; + right: 0; + position: absolute; + text-align: center; + span { + display: block; + position: absolute; + left: 0; + right: 0; + top: 50%; + transform: translateY(-50%); + padding: 0 15px; + font-size: 14px; + } + } + input[type="radio"] + label { + position: absolute; + z-index: 1000; + cursor: default; + } + input[type="radio"] + label:before { + border-width: 0; + background-color: #fff; + top: 10px; + left: 10px; + height: 12px; + width: 12px; + } + input[type="radio"]:checked + label:before { + background: #fff; + border-width: 3px; + border-color: #1491c1; + } + input[type="radio"]:checked + label:after { + display: none; + } + .icon { + font-size: 48px; + text-align: center; + position: absolute; + top: 26px; + color: #fff; + width: 100%; + } + .big-radio-label { + top: 96px; + position: relative; + background-color: #3FAE2A; + color: #fff; + padding: 10px 20px; //same as button padding + text-align: center; + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + white-space: nowrap; } } - table > tbody > tr > td { - vertical-align: middle; + + .big-radio:hover { + input[type="radio"]:not(:checked) + label:before { + background-color: #1491c1; + } + .description { + visibility: visible; + } } - .remove-icon { - color: red; - margin: 30px 0; - text-align: center; - display: inline-block; + } + + table > tbody > tr > td { + vertical-align: middle; + } + .remove-icon { + color: red; + margin: 30px 0; + text-align: center; + display: inline-block; + &.disabled { + color: grey; + } + } + .repo-url input { + width: 90%; + } + #skip-validation, #use-redhat { + label { + color: #666; + font-weight: normal; + margin-top: -2px; &.disabled { - color: grey; + opacity: 0.7; } } - .repo-url input { - width: 90%; + } + +} + +#customMpackRepos, +#downloadMpacks { + .table.table-hover > tbody { + > tr > td { + vertical-align: middle; + line-height: 20px; } - #skip-validation, #use-redhat { - label { - color: #666; - font-weight: normal; - margin-top: -2px; - &.disabled { - opacity: 0.7; - } - } + } +} + +#customProductRepos, +#verifyProductRepos { + .table.table-hover > tbody { + > tr > td { + border-width: 0; + line-height: 20px; + } + > tr, > tr:hover > td { + border-width: 0; + } + > tr.mpack-firstOs.os-firstRepo { + border-top-width: 1px; + } + > tr:last-of-type { + border-bottom-width: 1px; + } + } +} + +#downloadMpacks, +#verifyProductRepos { + .progress { + width: 75%; + margin: 0; + .progress-bar { + width: 100%; } } } @@ -731,9 +1039,6 @@ .wizard-nav { width: @wizard-side-nav-width; } - .wizard-content.col-md-9 { - width: calc(~"100% - 250px"); - } } } @@ -752,9 +1057,6 @@ .wizard-nav { width: @wizard-side-nav-width; } - .wizard-content.col-md-9 { - width: calc(~"100% - 250px"); - } } } } @@ -777,3 +1079,17 @@ } } +#useCaseHeader { + flex: auto; + margin-right: 10px; + text-align: left; +} + +.dropdown-menu input[type="checkbox"]:checked + label:after, +.table input[type="checkbox"]:checked + label:after { + line-height: 2; +} + +.filter-input { + margin: 15px; +} diff --git a/ambari-web/app/templates.js b/ambari-web/app/templates.js index b18cc732635..8df1fdb7227 100644 --- a/ambari-web/app/templates.js +++ b/ambari-web/app/templates.js @@ -22,6 +22,7 @@ require('templates/main/service/info/summary/base'); require('templates/common/progress'); +require('templates/common/mpackComparison'); require('templates/main/alerts/alert_instance/status'); require("templates/main/service/widgets/create/step2_number"); require("templates/main/service/widgets/create/step2_template"); diff --git a/ambari-web/app/templates/common/assign_master_components.hbs b/ambari-web/app/templates/common/assign_master_components.hbs index 70957424d54..f9124cc0083 100644 --- a/ambari-web/app/templates/common/assign_master_components.hbs +++ b/ambari-web/app/templates/common/assign_master_components.hbs @@ -19,11 +19,15 @@ {{#if view.showTitle}}

    {{view.title}}

    {{/if}} -

    - {{{view.alertMessage}}} -

    + {{#if isSaved}} + + {{/if}} +
    +

    + {{{view.alertMessage}}} +

    {{#each msg in controller.generalErrorMessages}}
    {{msg}}
    {{/each}} diff --git a/ambari-web/app/templates/common/dashrow.hbs b/ambari-web/app/templates/common/dashrow.hbs new file mode 100644 index 00000000000..8071c499023 --- /dev/null +++ b/ambari-web/app/templates/common/dashrow.hbs @@ -0,0 +1,61 @@ +{{! +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +}} + +
    +
    +
    + {{#if view.headerClass}} + {{view view.headerClass}} + {{else}} + {{#if view.encodeHeader}} + {{view.header}} + {{else}} + {{{view.header}}} + {{/if}} + {{/if}} + {{#if view.hasMenu}} + + {{/if}} + +
    +
    +
    + {{#if view.bodyClass}} + {{view view.bodyClass}} + {{else}} + {{#if view.encodeBody}} + {{view.body}} + {{else}} + {{{view.body}}} + {{/if}} + {{/if}} +
    +
    +
    +
    \ No newline at end of file diff --git a/ambari-web/app/templates/common/modal_popup.hbs b/ambari-web/app/templates/common/modal_popup.hbs index afc5d9f9f96..715e4166ef0 100644 --- a/ambari-web/app/templates/common/modal_popup.hbs +++ b/ambari-web/app/templates/common/modal_popup.hbs @@ -18,7 +18,7 @@ -