Skip to content

Commit 2c74d80

Browse files
committed
initial script
1 parent d2a29a7 commit 2c74d80

File tree

1 file changed

+174
-0
lines changed

1 file changed

+174
-0
lines changed

remove_setup.py

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
from copy import deepcopy
2+
import collections.abc
3+
from diraccfg import CFG
4+
import difflib
5+
import pprint
6+
import zlib
7+
from DIRAC import initialize
8+
9+
import typer
10+
from pathlib import Path
11+
12+
13+
def merge_json(d, u):
14+
"""
15+
Merge together two json
16+
"""
17+
for k, v in u.items():
18+
if isinstance(v, collections.abc.Mapping):
19+
d[k] = merge_json(d.get(k, {}), v)
20+
else:
21+
d[k] = v
22+
return d
23+
24+
25+
def merge_cs_sections_json(root_section, default_setup, known_vos, name=None, merge_in=None):
26+
"""
27+
Recursively merge the Operation sections
28+
:param root_section: start of the section we are looking at
29+
:param name: The name of the section, just used for printing
30+
:param merge_in: name of the section to merge in (if set, do not merge in the root). Only used for Defaults
31+
"""
32+
print(f"Working on {name}")
33+
# Create a Defaults section if it doesn't exist
34+
if merge_in:
35+
if merge_in not in root_section:
36+
root_section[merge_in] = {}
37+
merge_root = root_section[merge_in]
38+
else:
39+
merge_root = root_section
40+
for section in list(root_section):
41+
# Can only happen when merge_in is Default
42+
if section == merge_in:
43+
continue
44+
elif section == default_setup:
45+
print(f"Merging {default_setup} to {merge_in}")
46+
merge_json(merge_root, root_section[default_setup])
47+
elif section in known_vos:
48+
print(f"{section} is a vo.")
49+
merge_cs_sections_json(root_section[section], default_setup, known_vos, name=section)
50+
else:
51+
print(f"{section} is unknown, not touching it (other Setup, custom entry...)")
52+
53+
54+
def merge_cs_sections_cfg(cs_cfg: CFG, default_setup, known_vos):
55+
"""Do the merge of the Operation sections using the CFG object directly
56+
:param cs_cfg: root of the CS
57+
"""
58+
operation_section = cs_cfg.getAsCFG("/Operations")
59+
for section in list(operation_section.getAsDict()):
60+
# Can only happen when merge_in is Default
61+
if section == "Defaults":
62+
continue
63+
if section == default_setup:
64+
print(f"Merging {default_setup} to Defaults")
65+
66+
merged_section = operation_section["Defaults"].mergeWith(operation_section[default_setup])
67+
operation_section.deleteKey("Defaults")
68+
operation_section.createNewSection(
69+
"Defaults",
70+
comment="Automatic merging to remove Setups",
71+
contents=merged_section,
72+
)
73+
74+
# print(f"Removing {default_setup}")
75+
# operation_section.deleteKey(default_setup)
76+
elif section in known_vos:
77+
vo_section = operation_section[section]
78+
if default_setup in vo_section:
79+
merged_section = vo_section.mergeWith(vo_section[default_setup])
80+
operation_section.deleteKey(section)
81+
operation_section.createNewSection(
82+
section,
83+
comment="Automatic merging to remove Setups",
84+
contents=merged_section,
85+
)
86+
87+
print(f"{section} is a vo.")
88+
89+
else:
90+
print(f"{section} is unknown, not touching it (other Setup, custom entry...)")
91+
92+
cs_cfg.deleteKey("/Operations")
93+
cs_cfg.createNewSection("/Operations", comment="Merging", contents=operation_section)
94+
95+
96+
def compare_dicts(d1, d2):
97+
"""Produces an HTML output of the diff between 2 dicts"""
98+
return difflib.HtmlDiff().make_file(
99+
pprint.pformat(d1).splitlines(),
100+
pprint.pformat(d2).splitlines(),
101+
)
102+
103+
104+
def main(diff_file: Path = "/tmp/diff.html", setup: str = "", execute_update: bool = False):
105+
"""
106+
Remove the Setup sections from the Operations section.
107+
108+
If --diff-file is specified, dump the html comparison at this location
109+
110+
If --setup is specified, use it instead of the default one
111+
112+
If --execute-update is set, it will attempt to actually update the CS
113+
"""
114+
initialize()
115+
from DIRAC.ConfigurationSystem.Client.ConfigurationClient import ConfigurationClient
116+
from DIRAC.ConfigurationSystem.Client.ConfigurationData import gConfigurationData
117+
118+
cs_cfg = gConfigurationData.getRemoteCFG()
119+
120+
original_cs_json = cs_cfg.getAsDict()
121+
122+
cs_json = cs_cfg.getAsDict()
123+
124+
default_setup = setup if setup else cs_json["DIRAC"]["DefaultSetup"]
125+
typer.echo(f"Default setup is {default_setup}")
126+
known_vos = cs_json["Registry"]["VO"]
127+
typer.echo(f"Known VOs {known_vos}")
128+
master_cs = cs_json["DIRAC"]["Configuration"]["MasterServer"]
129+
typer.echo(f"MasterCS {master_cs}")
130+
131+
# First do a merge using JSON
132+
merge_cs_sections_json(cs_json["Operations"], default_setup, known_vos, "Operations", "Defaults")
133+
first_pass_cs_json = deepcopy(cs_json)
134+
# Do it twice, because runing over it twice shouldn't change the result
135+
merge_cs_sections_json(cs_json["Operations"], default_setup, known_vos, "Operations", "Defaults")
136+
# Make sure the output of the first and second pass are the same
137+
assert first_pass_cs_json == cs_json
138+
139+
##############
140+
141+
# Redo the exercise with the CFG object
142+
143+
merge_cs_sections_cfg(cs_cfg, default_setup, known_vos)
144+
first_pass_cs_cfg = cs_cfg.clone()
145+
merge_cs_sections_cfg(cs_cfg, default_setup, known_vos)
146+
assert first_pass_cs_cfg.getAsDict() == cs_cfg.getAsDict()
147+
148+
##############
149+
150+
# Finally, make sure we get the same thing in json and CFG
151+
152+
assert cs_cfg.getAsDict() == cs_json
153+
154+
##############
155+
156+
# Produces diff output
157+
158+
# print(compare_dicts(original_cs_json["Operations"], cs_cfg.getAsDict("/Operations")))
159+
with open(diff_file, "w") as f:
160+
f.write(compare_dicts(original_cs_json["Operations"], cs_json["Operations"]))
161+
162+
typer.echo(f"Diff written in {diff_file}")
163+
164+
if execute_update:
165+
compressed_data = zlib.compress(str(cs_cfg).encode(), 9)
166+
update_res = ConfigurationClient(url=master_cs).commitNewData(compressed_data)
167+
if update_res["OK"]:
168+
typer.echo("Successfuly updated CS")
169+
else:
170+
typer.echo(f"Error updating CS: {update_res[['Message']]}")
171+
172+
173+
if __name__ == "__main__":
174+
typer.run(main)

0 commit comments

Comments
 (0)