You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ja...@apache.org on 2016/12/07 21:39:50 UTC
ambari git commit: AMBARI-18941. When HS2Interactive is enabled,
any hive config change results in a CS queue refresh. (vivek
Subramanian via Jaimin)
Repository: ambari
Updated Branches:
refs/heads/trunk d1293e018 -> 96e69d58e
AMBARI-18941. When HS2Interactive is enabled, any hive config change results in a CS queue refresh. (vivek Subramanian via Jaimin)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/96e69d58
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/96e69d58
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/96e69d58
Branch: refs/heads/trunk
Commit: 96e69d58ee2b70123331a1b59450e1dfbeb4eccb
Parents: d1293e0
Author: Jaimin Jetly <ja...@hortonworks.com>
Authored: Wed Dec 7 13:40:03 2016 -0800
Committer: Jaimin Jetly <ja...@hortonworks.com>
Committed: Wed Dec 7 13:40:03 2016 -0800
----------------------------------------------------------------------
ambari-web/app/controllers/main/service.js | 67 ++-------------
ambari-web/app/messages.js | 2 +
.../configs/component_actions_by_configs.js | 64 ++++++++++++++
.../app/models/configs/theme/config_action.js | 32 ++++++-
ambari-web/app/templates/common/modal_popup.hbs | 11 ++-
ambari-web/app/utils/ajax/ajax.js | 15 +++-
.../app/utils/batch_scheduled_requests.js | 88 ++++----------------
ambari-web/app/views/common/modal_popup.js | 3 +
.../common/modal_popups/confirmation_popup.js | 5 +-
.../test/controllers/main/service_test.js | 23 -----
.../component_actions_by_configs_test.js | 3 +
.../test/utils/batch_scheduled_requests_test.js | 23 -----
12 files changed, 142 insertions(+), 194 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/96e69d58/ambari-web/app/controllers/main/service.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service.js b/ambari-web/app/controllers/main/service.js
index d6fbfbb..2677723 100644
--- a/ambari-web/app/controllers/main/service.js
+++ b/ambari-web/app/controllers/main/service.js
@@ -323,68 +323,11 @@ App.MainServiceController = Em.ArrayController.extend({
* @returns {$.ajax}
*/
restartHostComponents: function () {
- var batches, hiveInteractive = App.HostComponent.find().findProperty('componentName', 'HIVE_SERVER_INTERACTIVE');
- var isYARNQueueRefreshRequired = hiveInteractive && hiveInteractive.get('staleConfigs');
- var ajaxData = {
- "RequestInfo": {
- "command": "RESTART",
- "context": "Restart all required services",
- "operation_level": "host_component"
- },
- "Requests/resource_filters": [
- {
- "hosts_predicate": "HostRoles/stale_configs=true"
- }
- ]
- };
-
- if (isYARNQueueRefreshRequired) {
- batches = [
- {
- "order_id": 1,
- "type": "POST",
- "uri": App.apiPrefix + "/clusters/" + App.get('clusterName') + "/requests",
- "RequestBodyInfo": {
- "RequestInfo": {
- "context": "Refresh YARN Capacity Scheduler",
- "command": "REFRESHQUEUES",
- "parameters/forceRefreshConfigTags": "capacity-scheduler"
- },
- "Requests/resource_filters": [{
- "service_name": "YARN",
- "component_name": "RESOURCEMANAGER",
- "hosts": App.HostComponent.find().findProperty('componentName', 'RESOURCEMANAGER').get('hostName')
- }]
- }
- },
- {
- "order_id": 2,
- "type": "POST",
- "uri": App.apiPrefix + "/clusters/" + App.get('clusterName') + "/requests",
- "RequestBodyInfo": ajaxData
- }
- ];
-
- App.ajax.send({
- name: 'common.batch.request_schedules',
- sender: this,
- data: {
- intervalTimeSeconds: 1,
- tolerateSize: 0,
- batches: batches
- },
- success: 'restartAllRequiredSuccessCallback'
- });
- } else {
- App.ajax.send({
- name: 'request.post',
- sender: this,
- data: {
- data: ajaxData
- },
- success: 'restartAllRequiredSuccessCallback'
- });
- }
+ App.ajax.send({
+ name: 'restart.staleConfigs',
+ sender: this,
+ success: 'restartAllRequiredSuccessCallback'
+ });
},
/**
http://git-wip-us.apache.org/repos/asf/ambari/blob/96e69d58/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 595cd5d..731b8e3 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -429,6 +429,8 @@ Em.I18n.translations = {
'popup.highlight':'click to highlight',
'popup.confirmation.commonHeader':'Confirmation',
+ 'popup.confirmation.refreshYarnQueues.body':'It is strongly recommended to <strong>Refresh Yarn Queues</strong> after making a change to the capacity scheduler configuration. Would you like to proceed?',
+ 'popup.confirmation.refreshYarnQueues.buttonText':'Refresh Yarn Queues',
'popup.prompt.commonHeader':'Prompt',
'popup.confirmationFeedBack.sending':'Sending...',
'popup.confirmationFeedBack.query.fail':'Request failed',
http://git-wip-us.apache.org/repos/asf/ambari/blob/96e69d58/ambari-web/app/mixins/main/service/configs/component_actions_by_configs.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/main/service/configs/component_actions_by_configs.js b/ambari-web/app/mixins/main/service/configs/component_actions_by_configs.js
index c7bb047..347bdde 100644
--- a/ambari-web/app/mixins/main/service/configs/component_actions_by_configs.js
+++ b/ambari-web/app/mixins/main/service/configs/component_actions_by_configs.js
@@ -26,6 +26,13 @@ var stringUtils = require('utils/string_utils');
*/
App.ComponentActionsByConfigs = Em.Mixin.create({
+ configAction: null,
+
+ /**
+ * Flag to check if Yarn Queues have been refreshed already
+ */
+ isYarnQueueRefreshed: false,
+
/**
* Do component add/delete actions as inferred from value of service configs
* @public
@@ -34,11 +41,66 @@ App.ComponentActionsByConfigs = Em.Mixin.create({
doConfigActions: function () {
var serviceConfigs = this.get('stepConfigs').findProperty('serviceName', this.get('content.serviceName')).get('configs');
var configActionComponents = serviceConfigs.filterProperty('configActionComponent');
+ this.isYarnQueueRefreshed = false;
this.doComponentDeleteActions(configActionComponents);
this.doComponentAddActions(configActionComponents);
+ this.showPopup();
},
/**
+ * Method to show confirmation popup before sending an ajax request
+ */
+ showPopup: function() {
+ var config_actions = App.ConfigAction.find().filterProperty('actionType', 'showPopup');
+ var self = this;
+ if (config_actions.length) {
+ config_actions.forEach(function (config_action) {
+ var configs = self.get('allConfigs').filterProperty('filename', config_action.get('fileName')).filter(function (item) {
+ return item.get('value') !== item.get('initialValue');
+ });
+
+ if (configs.length) {
+ if(config_action.get('fileName') === 'capacity-scheduler.xml' && !self.isYarnQueueRefreshed) {
+ self.configAction = config_action;
+ App.showConfirmationPopup(function () {
+ self.popupPrimaryButtonCallback(config_action);
+ }, config_action.get('popupProperties').body, null, Em.I18n.t('popup.confirmation.commonHeader'), config_action.get('popupProperties').primaryButton.label, false, 'refresh_yarn_queues')
+ }
+ }
+ });
+ }
+ },
+
+ popupPrimaryButtonCallback: function (config_action) {
+ var hosts = App.Service.find(config_action.get('serviceName')).get('hostComponents').filterProperty('componentName', config_action.get('componentName')).mapProperty('hostName');
+ var self = this;
+ App.ajax.send({
+ name : config_action.get('popupProperties').primaryButton.metaData.name,
+ sender: self,
+ data : {
+ command : config_action.get('popupProperties').primaryButton.metaData.command,
+ context : config_action.get('popupProperties').primaryButton.metaData.context,
+ hosts : hosts.join(','),
+ serviceName : config_action.get('serviceName'),
+ componentName : config_action.get('componentName'),
+ forceRefreshConfigTags : config_action.get('configName')
+ },
+ error : 'popupPrimaryButtonCallbackError'
+ });
+ },
+
+ popupPrimaryButtonCallbackError: function(data) {
+ var error = this.configAction.get('popupProperties').errorMessage;
+ if(data && data.responseText){
+ try {
+ var json = $.parseJSON(data.responseText);
+ error += json.message;
+ } catch (err) {}
+ }
+ App.showAlertPopup(this.configAction.get('popupProperties').errorMessage, error, null);
+ },
+
+ /**
* Method informs if any component will be added/deleted on saving configurations
* @return {boolean}
* @public
@@ -352,6 +414,8 @@ App.ComponentActionsByConfigs = Em.Mixin.create({
var hostNames = App.Service.find(serviceName).get('hostComponents')
.filterProperty('componentName', componentName)
.mapProperty('hostName');
+ // Set the flag to true
+ this.isYarnQueueRefreshed = true;
batches.push({
"type": 'POST',
"uri": App.get('apiPrefix') + "/clusters/" + App.get('clusterName') + "/requests",
http://git-wip-us.apache.org/repos/asf/ambari/blob/96e69d58/ambari-web/app/models/configs/theme/config_action.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/configs/theme/config_action.js b/ambari-web/app/models/configs/theme/config_action.js
index 70c1e99..9ac4e7a 100644
--- a/ambari-web/app/models/configs/theme/config_action.js
+++ b/ambari-web/app/models/configs/theme/config_action.js
@@ -37,8 +37,6 @@ App.ConfigAction = DS.Model.extend({
*/
fileName: DS.attr('string'),
-
-
/**
* conditional String which can be evaluated to boolean result.
* If evaluated result of this staring is true then use the statement provided by `then` attribute.
@@ -47,8 +45,14 @@ App.ConfigAction = DS.Model.extend({
if: DS.attr('string'),
then: DS.attr('string'),
else: DS.attr('string'),
- hostComponentConfig: DS.attr('object')
-
+ hostComponentConfig: DS.attr('object', {
+ defaultValue: function () { return {}; }
+ }),
+ actionType: DS.attr('string'),
+ popupProperties: DS.attr('object', {
+ defaultValue: function () { return {}; }
+ }),
+ serviceName: DS.attr('string')
});
App.ConfigAction.FIXTURES = [
@@ -64,5 +68,25 @@ App.ConfigAction.FIXTURES = [
configName: "hive_server_interactive_host",
fileName: "hive-interactive-env.xml"
}
+ },
+ {
+ id: 2,
+ service_name: 'YARN',
+ component_name: 'RESOURCEMANAGER',
+ config_name: "capacity-scheduler",
+ file_name: "capacity-scheduler.xml",
+ action_type: "showPopup",
+ popup_properties: {
+ primaryButton: {
+ label: Em.I18n.t('popup.confirmation.refreshYarnQueues.buttonText'),
+ metaData: {
+ name: 'service.item.refreshQueueYarnRequest',
+ command: "REFRESHQUEUES",
+ context: Em.I18n.t('services.service.actions.run.yarnRefreshQueues.context')
+ }
+ },
+ body: Em.I18n.t('popup.confirmation.refreshYarnQueues.body'),
+ errorMessage: Em.I18n.t('services.service.actions.run.yarnRefreshQueues.error')
+ }
}
];
http://git-wip-us.apache.org/repos/asf/ambari/blob/96e69d58/ambari-web/app/templates/common/modal_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/common/modal_popup.hbs b/ambari-web/app/templates/common/modal_popup.hbs
index 863eb44..59e9c0b 100644
--- a/ambari-web/app/templates/common/modal_popup.hbs
+++ b/ambari-web/app/templates/common/modal_popup.hbs
@@ -47,7 +47,6 @@
{{/if}}
{{/if}}
</div>
-
{{! Modal footer }}
{{#if view.showFooter}}
{{#if view.footerClass}}
@@ -55,17 +54,17 @@
{{else}}
<div class="modal-footer">
{{#if view.third}}
- <button {{bindAttr disabled="view.disableThird" class=":btn view.thirdClass"}} {{action onThird target="view"}}>{{view.third}}</button>
+ <button {{bindAttr disabled="view.disableThird" class=":btn view.thirdClass" id="view.thirdId"}} {{action onThird target="view"}}>{{view.third}}</button>
{{/if}}
{{#if view.secondary}}
- <button {{bindAttr disabled="view.disableSecondary" class=":btn view.secondaryClass"}} {{action onSecondary target="view"}}>{{view.secondary}}</button>
+ <button {{bindAttr disabled="view.disableSecondary" class=":btn view.secondaryClass" id="view.secondaryId"}} {{action onSecondary target="view"}}>{{view.secondary}}</button>
{{/if}}
{{#if view.primary}}
- <button {{bindAttr disabled="view.disablePrimary" class=":btn view.primaryClass"}} {{action onPrimary target="view"}}>{{view.primary}}</button>
+ <button {{bindAttr disabled="view.disablePrimary" class=":btn view.primaryClass" id="view.primaryId"}} {{action onPrimary target="view"}}>{{view.primary}}</button>
{{/if}}
</div>
- {{/if}}
- {{/if}}
+ {{/if}}
+ {{/if}}
</div>
</div>
</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/96e69d58/ambari-web/app/utils/ajax/ajax.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ajax/ajax.js b/ambari-web/app/utils/ajax/ajax.js
index 09da497..5831521 100644
--- a/ambari-web/app/utils/ajax/ajax.js
+++ b/ambari-web/app/utils/ajax/ajax.js
@@ -2351,13 +2351,24 @@ var urls = {
}
},
- 'request.post': {
+ 'restart.staleConfigs': {
'real': "/clusters/{clusterName}/requests",
'mock': "",
'format': function (data) {
return {
type: 'POST',
- data: JSON.stringify(data.data)
+ data: JSON.stringify({
+ "RequestInfo": {
+ "command": "RESTART",
+ "context": "Restart all required services",
+ "operation_level": "host_component"
+ },
+ "Requests/resource_filters": [
+ {
+ "hosts_predicate": "HostRoles/stale_configs=true"
+ }
+ ]
+ })
}
}
},
http://git-wip-us.apache.org/repos/asf/ambari/blob/96e69d58/ambari-web/app/utils/batch_scheduled_requests.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/batch_scheduled_requests.js b/ambari-web/app/utils/batch_scheduled_requests.js
index c9828a4..b8ae955 100644
--- a/ambari-web/app/utils/batch_scheduled_requests.js
+++ b/ambari-web/app/utils/batch_scheduled_requests.js
@@ -203,16 +203,9 @@ module.exports = {
*/
var componentToHostsMap = {};
var hosts = [];
- var batches, refreshYARNQueues = false;
-
hostComponentsList.forEach(function(hc) {
var hostName = hc.get('hostName');
var componentName = hc.get('componentName');
-
- if (componentName === 'HIVE_SERVER_INTERACTIVE') {
- refreshYARNQueues = true;
- }
-
if (!componentToHostsMap[componentName]) {
componentToHostsMap[componentName] = [];
}
@@ -238,72 +231,21 @@ module.exports = {
if (resource_filters.length) {
- if (refreshYARNQueues) {
- batches = [
- {
- "order_id": 1,
- "type": "POST",
- "uri": App.apiPrefix + "/clusters/" + App.get('clusterName') + "/requests",
- "RequestBodyInfo": {
- "RequestInfo": {
- "context": "Refresh YARN Capacity Scheduler",
- "command": "REFRESHQUEUES",
- "parameters/forceRefreshConfigTags": "capacity-scheduler"
- },
- "Requests/resource_filters": [{
- "service_name": "YARN",
- "component_name": "RESOURCEMANAGER",
- "hosts": App.HostComponent.find().findProperty('componentName', 'RESOURCEMANAGER').get('hostName')
- }]
- }
- },
- {
- "order_id": 2,
- "type": "POST",
- "uri": App.apiPrefix + "/clusters/" + App.get('clusterName') + "/requests",
- "RequestBodyInfo": {
- "RequestInfo": {
- "context": context,
- "command": "RESTART",
- "operation_level": operation_level
- },
- "Requests/resource_filters": resource_filters
- }
- }
- ];
-
- App.ajax.send({
- name: 'common.batch.request_schedules',
- sender: {
- successCallback: defaultSuccessCallback,
- errorCallback: defaultErrorCallback
- },
- data: {
- intervalTimeSeconds: 1,
- tolerateSize: 0,
- batches: batches,
- query: query
- },
- success: 'successCallback',
- error: 'errorCallback'
- });
- } else {
- App.ajax.send({
- name: 'restart.hostComponents',
- sender: {
- successCallback: defaultSuccessCallback,
- errorCallback: defaultErrorCallback
- },
- data: {
- context: context,
- resource_filters: resource_filters,
- query: query,
- operation_level: operation_level
- },
- success: 'successCallback',
- error: 'errorCallback'
- });
- }
+ App.ajax.send({
+ name: 'restart.hostComponents',
+ sender: {
+ successCallback: defaultSuccessCallback,
+ errorCallback: defaultErrorCallback
+ },
+ data: {
+ context: context,
+ resource_filters: resource_filters,
+ query: query,
+ operation_level: operation_level
+ },
+ success: 'successCallback',
+ error: 'errorCallback'
+ });
}
},
http://git-wip-us.apache.org/repos/asf/ambari/blob/96e69d58/ambari-web/app/views/common/modal_popup.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/modal_popup.js b/ambari-web/app/views/common/modal_popup.js
index d5e033a..a643b23 100644
--- a/ambari-web/app/views/common/modal_popup.js
+++ b/ambari-web/app/views/common/modal_popup.js
@@ -45,6 +45,9 @@ App.ModalPopup = Ember.View.extend({
}
return modalDialogClasses.join(' ');
}.property('modalDialogClasses.[]'),
+ primaryId: '',
+ secondaryId: '',
+ thirdId: '',
onPrimary: function () {
this.hide();
},
http://git-wip-us.apache.org/repos/asf/ambari/blob/96e69d58/ambari-web/app/views/common/modal_popups/confirmation_popup.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/modal_popups/confirmation_popup.js b/ambari-web/app/views/common/modal_popups/confirmation_popup.js
index fbcc882..dc7f596 100644
--- a/ambari-web/app/views/common/modal_popups/confirmation_popup.js
+++ b/ambari-web/app/views/common/modal_popups/confirmation_popup.js
@@ -29,7 +29,7 @@ var App = require('app');
* @param {Boolean} isCritical
* @return {*}
*/
-App.showConfirmationPopup = function (primary, body, secondary, header, primaryText, isCritical) {
+App.showConfirmationPopup = function (primary, body, secondary, header, primaryText, isCritical, staticId) {
if (!primary) {
return false;
}
@@ -39,6 +39,9 @@ App.showConfirmationPopup = function (primary, body, secondary, header, primaryT
header: header || Em.I18n.t('popup.confirmation.commonHeader'),
body: body || Em.I18n.t('question.sure'),
primaryClass: isCritical ? 'btn-danger' : 'btn-success',
+ primaryId: staticId ? staticId + '_primary' : '',
+ secondaryId: staticId ? staticId + '_secondary' : '',
+ thirdId: staticId ? staticId + '_third' : '',
onPrimary: function () {
this.hide();
primary();
http://git-wip-us.apache.org/repos/asf/ambari/blob/96e69d58/ambari-web/test/controllers/main/service_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/service_test.js b/ambari-web/test/controllers/main/service_test.js
index 440c26e..0421a43 100644
--- a/ambari-web/test/controllers/main/service_test.js
+++ b/ambari-web/test/controllers/main/service_test.js
@@ -703,27 +703,4 @@ describe('App.MainServiceController', function () {
expect(mock.showPopup.calledOnce).to.be.true;
});
});
-
- describe('#restartHostComponents', function () {
-
- beforeEach(function () {
- this.mock = sinon.stub(App.HostComponent, 'find');
- });
- afterEach(function () {
- App.HostComponent.find.restore();
- });
-
- it('should make batch request to refresh YARN queues', function () {
- this.mock.returns([Em.Object.create({componentName: 'HIVE_SERVER_INTERACTIVE', staleConfigs: true}), Em.Object.create({componentName: 'RESOURCEMANAGER', staleConfigs: false})]);
- mainServiceController.restartHostComponents();
- expect(testHelpers.findAjaxRequest('name', 'common.batch.request_schedules')).not.to.be.undefined;
- });
-
- it('should make single request without refresh YARN queues', function () {
- this.mock.returns([Em.Object.create({componentName: 'HIVE_SERVER_INTERACTIVE', staleConfigs: false}), Em.Object.create({componentName: 'RESOURCEMANAGER', staleConfigs: false})]);
- mainServiceController.restartHostComponents();
- expect(testHelpers.findAjaxRequest('name', 'request.post')).not.to.be.undefined;
- });
-
- });
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/96e69d58/ambari-web/test/mixins/main/service/configs/component_actions_by_configs_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/mixins/main/service/configs/component_actions_by_configs_test.js b/ambari-web/test/mixins/main/service/configs/component_actions_by_configs_test.js
index a6877fe..1a93a26 100644
--- a/ambari-web/test/mixins/main/service/configs/component_actions_by_configs_test.js
+++ b/ambari-web/test/mixins/main/service/configs/component_actions_by_configs_test.js
@@ -37,6 +37,9 @@ describe('App.ComponentActionsByConfigs', function () {
beforeEach(function() {
sinon.stub(mixin, 'doComponentDeleteActions');
sinon.stub(mixin, 'doComponentAddActions');
+ mixin.set('allConfigs', [Em.Object.create({
+ filename: ''
+ })]);
mixin.set('stepConfigs', [Em.Object.create({
serviceName: 'S1',
configs: [{
http://git-wip-us.apache.org/repos/asf/ambari/blob/96e69d58/ambari-web/test/utils/batch_scheduled_requests_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/utils/batch_scheduled_requests_test.js b/ambari-web/test/utils/batch_scheduled_requests_test.js
index 20b190b..c282350 100644
--- a/ambari-web/test/utils/batch_scheduled_requests_test.js
+++ b/ambari-web/test/utils/batch_scheduled_requests_test.js
@@ -20,8 +20,6 @@ require('utils/helper');
require('views/common/rolling_restart_view');
var batchUtils = require('utils/batch_scheduled_requests');
var modelSetup = require('test/init_model_test');
-var testHelpers = require('test/helpers');
-
describe('batch_scheduled_requests', function() {
beforeEach(function(){
@@ -133,25 +131,4 @@ describe('batch_scheduled_requests', function() {
});
- describe('#restartHostComponents', function () {
-
- beforeEach(function () {
- sinon.stub(App.HostComponent, 'find').returns([Em.Object.create({componentName: 'RESOURCEMANAGER', hostName: '1'})]);
- });
- afterEach(function () {
- App.HostComponent.find.restore();
- });
-
- it('should make batch request to refresh YARN queues', function () {
- batchUtils.restartHostComponents([Em.Object.create({componentName: 'HIVE_SERVER_INTERACTIVE'})]);
- expect(testHelpers.findAjaxRequest('name', 'common.batch.request_schedules')).to.exists;
- });
-
- it('should make single request without refresh YARN queues', function () {
- batchUtils.restartHostComponents([Em.Object.create({componentName: 'NAMENODE'})]);
- expect(testHelpers.findAjaxRequest('name', 'restart.hostComponents')).to.exists;
- });
-
- });
-
});