forked from ClusterLabs/pcs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tools.py
154 lines (129 loc) · 3.89 KB
/
tools.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import os
import tempfile
import uuid
from contextlib import contextmanager
from typing import (
IO,
Callable,
ContextManager,
Generator,
Literal,
Mapping,
Optional,
TypeVar,
Union,
overload,
)
from pcs.common import reports
from pcs.lib.errors import LibraryError
T = TypeVar("T")
def get_optional_value(
constructor: Callable[[str], T], value: Optional[str]
) -> Optional[T]:
if value is None:
return None
return constructor(value)
def generate_binary_key(random_bytes_count: int) -> bytes:
return os.urandom(random_bytes_count)
def generate_uuid() -> str:
return uuid.uuid4().hex
def environment_file_to_dict(config: str) -> dict[str, str]:
"""
Parse systemd Environment file. This parser is simplified version of
parser in systemd, because of their poor implementation.
Returns configuration in dictionary in format:
{
<option>: <value>,
...
}
config -- Environment file as string
"""
# escape new lines
config = config.replace("\\\n", "")
data = {}
for line in [l.strip() for l in config.split("\n")]:
if line == "" or line.startswith("#") or line.startswith(";"):
continue
if "=" not in line:
continue
key, val = line.split("=", 1)
value = val.strip()
data[key.strip()] = value
return data
def dict_to_environment_file(config_dict: Mapping[str, str]) -> str:
"""
Convert data in dictionary to Environment file format.
Returns Environment file as string in format:
# comment
<option>=<value>
...
config_dict -- dictionary in format: { <option>: <value>, ...}
"""
lines = ["# This file has been generated by pcs.\n"]
for key, val in sorted(config_dict.items()):
lines.append(f"{key}={val}\n")
return "".join(lines)
@overload
def get_tmp_file(
data: Optional[bytes], binary: Literal[True]
) -> ContextManager[IO[bytes]]:
pass
@overload
def get_tmp_file(
data: Optional[str],
binary: Literal[False] = False,
) -> ContextManager[IO[str]]:
pass
# We ignore return type here as it doesn't work with mypy (@contextmanager and
# @overload) and it is properly typed in @overload functions.
@contextmanager
def get_tmp_file( # type: ignore
data: Optional[Union[bytes, str]],
binary: bool = False,
):
mode = "w+b" if binary else "w+"
tmpfile = None
try:
with tempfile.NamedTemporaryFile(mode=mode, suffix=".pcs") as tmpfile:
if data is not None:
tmpfile.write(data)
tmpfile.flush()
yield tmpfile
finally:
if tmpfile:
tmpfile.close()
@contextmanager
def get_tmp_cib(
report_processor: reports.ReportProcessor, data: Optional[str]
) -> Generator[IO[str], None, None]:
try:
with get_tmp_file(data) as tmp_cib_file:
report_processor.report(
reports.ReportItem.debug(
reports.messages.TmpFileWrite(tmp_cib_file.name, data or "")
)
)
yield tmp_cib_file
except EnvironmentError as e:
raise LibraryError(
reports.ReportItem.error(reports.messages.CibSaveTmpError(str(e)))
) from e
def create_tmp_cib(
report_processor: reports.ReportProcessor, data: Optional[str]
) -> IO[str]:
try:
# pylint: disable=consider-using-with
tmp_file = tempfile.NamedTemporaryFile(mode="w+", suffix=".pcs")
if data is not None:
tmp_file.write(data)
tmp_file.flush()
report_processor.report(
reports.ReportItem.debug(
reports.messages.TmpFileWrite(tmp_file.name, data or "")
)
)
return tmp_file
except EnvironmentError as e:
raise LibraryError(
reports.ReportItem.error(reports.messages.CibSaveTmpError(str(e)))
) from e