diff --git a/python/cm/adcm_config.py b/python/cm/adcm_config.py index e691d26aae..aae3b9cd0f 100644 --- a/python/cm/adcm_config.py +++ b/python/cm/adcm_config.py @@ -193,7 +193,8 @@ def get_prototype_config(proto, action=None): return (spec, flat_spec, conf, attr) -def switch_config(obj, new_proto, old_proto): # pylint: disable=too-many-locals,too-many-branches +def switch_config(obj, new_proto, old_proto): # pylint: disable=too-many-locals,too-many-branches,too-many-statements + # process objects without config if not obj.config: spec, _, conf, attr = get_prototype_config(new_proto) obj_conf = init_object_config(spec, conf, attr) @@ -217,15 +218,19 @@ def is_new_default(key): return True return False + # set new default config values and gather information about activatable groups new_conf = {} + active_groups = {} inactive_groups = {} for key in new_spec: if new_spec[key].type == 'group': limits = {} if new_spec[key].limits: limits = json.loads(new_spec[key].limits) - if 'activatable' in limits: - if 'active' in limits and not limits['active']: + if 'activatable' in limits and 'active' in limits: + if limits['active']: + active_groups[key.rstrip('/')] = True + else: inactive_groups[key.rstrip('/')] = True continue if key in old_spec: @@ -236,6 +241,7 @@ def is_new_default(key): else: new_conf[key] = get_default(new_spec[key], new_proto) + # go from flat config to 2-level dictionary unflat_conf = {} for key in new_conf: k1, k2 = key.split('/') @@ -246,12 +252,18 @@ def is_new_default(key): unflat_conf[k1] = {} unflat_conf[k1][k2] = new_conf[key] - # skip inactive groups in new prototype config + # skip inactive groups and set attributes for new config + attr = {} + if cl.attr: + attr = json.loads(cl.attr) for key in unflat_conf: + if key in active_groups: + attr[key] = {'active': True} if key in inactive_groups: unflat_conf[key] = None + attr[key] = {'active': False} - save_obj_config(obj.config, unflat_conf, 'upgrade', cl.attr) + save_obj_config(obj.config, unflat_conf, 'upgrade', json.dumps(attr)) process_file_type(obj, new_unflat_spec, unflat_conf) diff --git a/python/cm/tests_upgrade.py b/python/cm/tests_upgrade.py index fff12e2ee9..c8581bcd60 100644 --- a/python/cm/tests_upgrade.py +++ b/python/cm/tests_upgrade.py @@ -142,8 +142,11 @@ def cook_upgrade(self, bundle): def get_config(obj): + attr = {} cl = ConfigLog.objects.get(obj_ref=obj.config, id=obj.config.current) - return json.loads(cl.config) + if cl.attr: + attr = json.loads(cl.attr) + return json.loads(cl.config), attr class TestConfigUpgrade(TestCase): @@ -168,7 +171,7 @@ def test_empty_first_config(self): cluster = cm.api.add_cluster(proto1, 'Cluster1') self.assertEqual(cluster.config, None) cm.adcm_config.switch_config(cluster, proto2, proto1) - new_config = get_config(cluster) + new_config, _ = get_config(cluster) self.assertEqual(new_config['port'], 42) def test_adding_parameter(self): @@ -177,10 +180,10 @@ def test_adding_parameter(self): self.add_conf(prototype=proto2, name='host', type='string', default='arenadata.com') self.add_conf(prototype=proto2, name='port', type='integer', default=42) cluster = cm.api.add_cluster(proto1, 'Cluster1') - old_conf = get_config(cluster) + old_conf, _ = get_config(cluster) self.assertEqual(old_conf, {'host': 'arenadata.com'}) cm.adcm_config.switch_config(cluster, proto2, proto1) - new_config = get_config(cluster) + new_config, _ = get_config(cluster) self.assertEqual(new_config, {'host': 'arenadata.com', 'port': 42}) def test_deleting_parameter(self): @@ -188,10 +191,10 @@ def test_deleting_parameter(self): self.add_conf(prototype=proto1, name='host', type='string', default='arenadata.com') self.add_conf(prototype=proto2, name='port', type='integer', default=42) cluster = cm.api.add_cluster(proto1, 'Cluster1') - old_conf = get_config(cluster) + old_conf, _ = get_config(cluster) self.assertEqual(old_conf, {'host': 'arenadata.com'}) cm.adcm_config.switch_config(cluster, proto2, proto1) - new_config = get_config(cluster) + new_config, _ = get_config(cluster) self.assertEqual(new_config, {'port': 42}) def test_default(self): @@ -199,10 +202,10 @@ def test_default(self): self.add_conf(prototype=proto1, name='port', type='integer', default=42) self.add_conf(prototype=proto2, name='port', type='integer', default=43) cluster = cm.api.add_cluster(proto1, 'Cluster1') - old_conf = get_config(cluster) + old_conf, _ = get_config(cluster) self.assertEqual(old_conf, {'port': 42}) cm.adcm_config.switch_config(cluster, proto2, proto1) - new_config = get_config(cluster) + new_config, _ = get_config(cluster) self.assertEqual(new_config, {'port': 43}) def test_non_default(self): @@ -210,11 +213,11 @@ def test_non_default(self): self.add_conf(prototype=proto1, name='port', type='integer', default=42) self.add_conf(prototype=proto2, name='port', type='integer', default=43) cluster = cm.api.add_cluster(proto1, 'Cluster1') - old_conf = get_config(cluster) + old_conf, _ = get_config(cluster) old_conf['port'] = 100500 cm.adcm_config.save_obj_config(cluster.config, old_conf) cm.adcm_config.switch_config(cluster, proto2, proto1) - new_config = get_config(cluster) + new_config, _ = get_config(cluster) self.assertEqual(new_config, {'port': 100500}) def test_add_group(self): @@ -224,10 +227,10 @@ def test_add_group(self): self.add_conf(prototype=proto2, name='advance', type='group') self.add_conf(prototype=proto2, name='advance', subname='port', type='integer', default=42) cluster = cm.api.add_cluster(proto1, 'Cluster1') - old_conf = get_config(cluster) + old_conf, _ = get_config(cluster) self.assertEqual(old_conf, {'host': 'arenadata.com'}) cm.adcm_config.switch_config(cluster, proto2, proto1) - new_config = get_config(cluster) + new_config, _ = get_config(cluster) self.assertEqual(new_config, {'host': 'arenadata.com', 'advance': {'port': 42}}) def test_add_non_active_group(self): @@ -238,11 +241,12 @@ def test_add_non_active_group(self): self.add_conf(prototype=proto2, name='advance', type='group', limits=json.dumps(limits)) self.add_conf(prototype=proto2, name='advance', subname='port', type='integer', default=42) cluster = cm.api.add_cluster(proto1, 'Cluster1') - old_conf = get_config(cluster) + old_conf, _ = get_config(cluster) self.assertEqual(old_conf, {'host': 'arenadata.com'}) cm.adcm_config.switch_config(cluster, proto2, proto1) - new_config = get_config(cluster) + new_config, new_attr = get_config(cluster) self.assertEqual(new_config, {'host': 'arenadata.com', 'advance': None}) + self.assertEqual(new_attr, {'advance': {'active': False}}) def test_add_active_group(self): (proto1, proto2) = self.cook_proto() @@ -252,11 +256,12 @@ def test_add_active_group(self): self.add_conf(prototype=proto2, name='advance', type='group', limits=json.dumps(limits)) self.add_conf(prototype=proto2, name='advance', subname='port', type='integer', default=42) cluster = cm.api.add_cluster(proto1, 'Cluster1') - old_conf = get_config(cluster) + old_conf, _ = get_config(cluster) self.assertEqual(old_conf, {'host': 'arenadata.com'}) cm.adcm_config.switch_config(cluster, proto2, proto1) - new_config = get_config(cluster) + new_config, new_attr = get_config(cluster) self.assertEqual(new_config, {'host': 'arenadata.com', 'advance': {'port': 42}}) + self.assertEqual(new_attr, {'advance': {'active': True}}) class TestUpgrade(TestCase): diff --git a/web/src/app/shared/components/actions/master/master.component.ts b/web/src/app/shared/components/actions/master/master.component.ts index ed2c3d825d..6f31c5c5e8 100644 --- a/web/src/app/shared/components/actions/master/master.component.ts +++ b/web/src/app/shared/components/actions/master/master.component.ts @@ -16,6 +16,7 @@ import { DynamicComponent, DynamicEvent } from '@app/shared/directives/dynamic.d import { BaseDirective } from '../../../directives/base.directive'; import { ActionParameters } from '../actions.directive'; import { IValue, MasterService, whatShow } from './master.service'; +import { ActionMasterConfigComponent } from './action-master-config.component'; @Component({ selector: 'app-master', @@ -41,6 +42,7 @@ export class ActionMasterComponent extends BaseDirective implements DynamicCompo show: whatShow; @ViewChild('runBtn', { read: ElementRef }) runBtn: ElementRef; + @ViewChild(ActionMasterConfigComponent) master: ActionMasterConfigComponent; constructor(private service: MasterService) { super(); @@ -59,7 +61,8 @@ export class ActionMasterComponent extends BaseDirective implements DynamicCompo return value && ((value.hostmap && value.hostmap.noValid) || (value.config && !value.config.form?.valid)); } - run(value: IValue) { + run(value: IValue = {}) { + value.attr = this.master?.fields?.attr; const data = this.service.parseData(value); this.service .send(this.action.run, data) diff --git a/web/src/app/shared/components/actions/master/master.service.ts b/web/src/app/shared/components/actions/master/master.service.ts index 5ce9249360..1ece1f2695 100644 --- a/web/src/app/shared/components/actions/master/master.service.ts +++ b/web/src/app/shared/components/actions/master/master.service.ts @@ -16,8 +16,10 @@ import { FieldService } from '@app/shared/configuration/field.service'; import { ConfigFieldsComponent } from '@app/shared/configuration/fields/fields.component'; import { ServiceHostComponent } from '@app/shared/host-components-map/services2hosts/service-host.component'; import { Post } from '@app/shared/host-components-map/types'; +import { IConfigAttr } from '@app/shared/configuration/types'; export interface IValue { + attr?: IConfigAttr; config?: ConfigFieldsComponent; hostmap?: ServiceHostComponent; } diff --git a/web/src/app/shared/configuration/fields/fields.component.ts b/web/src/app/shared/configuration/fields/fields.component.ts index 6fa6eb6ece..01a16c9ddb 100644 --- a/web/src/app/shared/configuration/fields/fields.component.ts +++ b/web/src/app/shared/configuration/fields/fields.component.ts @@ -56,6 +56,10 @@ export class ConfigFieldsComponent { constructor(private service: FieldService) {} + get attr() { + return this.dataOptions.filter((a) => a.type === 'group' && (a as PanelOptions).activatable).reduce((p, c: PanelOptions) => ({ ...p, [c.name]: { active: c.active } }), {}); + } + isPanel(item: FieldOptions | PanelOptions) { return 'options' in item && !item.hidden; } diff --git a/web/src/app/shared/configuration/main/main.component.ts b/web/src/app/shared/configuration/main/main.component.ts index 8ef4c7c976..cbf832cf1c 100644 --- a/web/src/app/shared/configuration/main/main.component.ts +++ b/web/src/app/shared/configuration/main/main.component.ts @@ -107,19 +107,13 @@ export class ConfigComponent extends SocketListenerDirective implements OnInit { ); } - getActivatableGroup() { - return this.fields.dataOptions - .filter((a) => a.type === 'group' && (a as PanelOptions).activatable) - .reduce((p, c: PanelOptions) => ({ ...p, [c.name]: { active: c.active } }), {}); - } - save() { const form = this.fields.form; if (form.valid) { this.saveFlag = true; this.historyComponent.reset(); const config = this.service.parseValue(this.fields.form.value, this.rawConfig.config); - const send = { config, attr: this.getActivatableGroup(), description: this.tools.description.value }; + const send = { config, attr: this.fields.attr, description: this.tools.description.value }; this.config$ = this.service.send(this.saveUrl, send).pipe( tap((c) => { this.saveFlag = false; diff --git a/web/src/app/shared/details/detail.component.ts b/web/src/app/shared/details/detail.component.ts index 643bc24a07..556f7ed3b0 100644 --- a/web/src/app/shared/details/detail.component.ts +++ b/web/src/app/shared/details/detail.component.ts @@ -72,7 +72,7 @@ export class DetailComponent extends SocketListenerDirective implements OnInit, this.status = status; const parent = w.current.typeName === 'cluster' ? null : w.cluster; - this.issue = parent?.issue || issue; + this.issue = issue; this.current = { parent, diff --git a/web/src/app/shared/details/left/left.component.ts b/web/src/app/shared/details/left/left.component.ts index 85308e116b..0b1103ef6a 100644 --- a/web/src/app/shared/details/left/left.component.ts +++ b/web/src/app/shared/details/left/left.component.ts @@ -36,7 +36,7 @@ export class LeftComponent { } @Input() set issue(i: Issue) { - this.items = this.items.map((a) => ({ ...a, issue: this.navigation.findIssue(a.url, i || {}) ? 'issue' : '' })); + if (i) this.items = this.items.map((a) => ({ ...a, issue: this.navigation.findIssue(a.url, i) ? 'issue' : '' })); } @Input() set status(v: number) {