pyinfra 0.11.dev3__py3-none-any.whl → 3.5.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyinfra/__init__.py +9 -12
- pyinfra/__main__.py +4 -0
- pyinfra/api/__init__.py +18 -3
- pyinfra/api/arguments.py +406 -0
- pyinfra/api/arguments_typed.py +79 -0
- pyinfra/api/command.py +274 -0
- pyinfra/api/config.py +222 -28
- pyinfra/api/connect.py +33 -13
- pyinfra/api/connectors.py +27 -0
- pyinfra/api/deploy.py +65 -66
- pyinfra/api/exceptions.py +67 -18
- pyinfra/api/facts.py +253 -202
- pyinfra/api/host.py +413 -50
- pyinfra/api/inventory.py +121 -160
- pyinfra/api/operation.py +432 -262
- pyinfra/api/operations.py +273 -260
- pyinfra/api/state.py +302 -248
- pyinfra/api/util.py +291 -368
- pyinfra/connectors/base.py +173 -0
- pyinfra/connectors/chroot.py +212 -0
- pyinfra/connectors/docker.py +381 -0
- pyinfra/connectors/dockerssh.py +297 -0
- pyinfra/connectors/local.py +238 -0
- pyinfra/connectors/scp/__init__.py +1 -0
- pyinfra/connectors/scp/client.py +204 -0
- pyinfra/connectors/ssh.py +670 -0
- pyinfra/connectors/ssh_util.py +114 -0
- pyinfra/connectors/sshuserclient/client.py +309 -0
- pyinfra/connectors/sshuserclient/config.py +102 -0
- pyinfra/connectors/terraform.py +135 -0
- pyinfra/connectors/util.py +410 -0
- pyinfra/connectors/vagrant.py +183 -0
- pyinfra/context.py +145 -0
- pyinfra/facts/__init__.py +7 -6
- pyinfra/facts/apk.py +22 -7
- pyinfra/facts/apt.py +117 -60
- pyinfra/facts/brew.py +100 -15
- pyinfra/facts/bsdinit.py +23 -0
- pyinfra/facts/cargo.py +37 -0
- pyinfra/facts/choco.py +47 -0
- pyinfra/facts/crontab.py +195 -0
- pyinfra/facts/deb.py +94 -0
- pyinfra/facts/dnf.py +48 -0
- pyinfra/facts/docker.py +96 -23
- pyinfra/facts/efibootmgr.py +113 -0
- pyinfra/facts/files.py +630 -58
- pyinfra/facts/flatpak.py +77 -0
- pyinfra/facts/freebsd.py +70 -0
- pyinfra/facts/gem.py +19 -6
- pyinfra/facts/git.py +59 -14
- pyinfra/facts/gpg.py +150 -0
- pyinfra/facts/hardware.py +313 -167
- pyinfra/facts/iptables.py +72 -62
- pyinfra/facts/launchd.py +44 -0
- pyinfra/facts/lxd.py +17 -4
- pyinfra/facts/mysql.py +122 -86
- pyinfra/facts/npm.py +17 -9
- pyinfra/facts/openrc.py +71 -0
- pyinfra/facts/opkg.py +246 -0
- pyinfra/facts/pacman.py +50 -7
- pyinfra/facts/pip.py +24 -7
- pyinfra/facts/pipx.py +82 -0
- pyinfra/facts/pkg.py +15 -6
- pyinfra/facts/pkgin.py +35 -0
- pyinfra/facts/podman.py +54 -0
- pyinfra/facts/postgres.py +178 -0
- pyinfra/facts/postgresql.py +6 -147
- pyinfra/facts/rpm.py +105 -0
- pyinfra/facts/runit.py +77 -0
- pyinfra/facts/selinux.py +161 -0
- pyinfra/facts/server.py +746 -285
- pyinfra/facts/snap.py +88 -0
- pyinfra/facts/systemd.py +139 -0
- pyinfra/facts/sysvinit.py +59 -0
- pyinfra/facts/upstart.py +35 -0
- pyinfra/facts/util/__init__.py +17 -0
- pyinfra/facts/util/databases.py +4 -6
- pyinfra/facts/util/packaging.py +37 -6
- pyinfra/facts/util/units.py +30 -0
- pyinfra/facts/util/win_files.py +99 -0
- pyinfra/facts/vzctl.py +20 -13
- pyinfra/facts/xbps.py +35 -0
- pyinfra/facts/yum.py +34 -40
- pyinfra/facts/zfs.py +77 -0
- pyinfra/facts/zypper.py +42 -0
- pyinfra/local.py +45 -83
- pyinfra/operations/__init__.py +12 -0
- pyinfra/operations/apk.py +98 -0
- pyinfra/operations/apt.py +488 -0
- pyinfra/operations/brew.py +231 -0
- pyinfra/operations/bsdinit.py +59 -0
- pyinfra/operations/cargo.py +45 -0
- pyinfra/operations/choco.py +61 -0
- pyinfra/operations/crontab.py +191 -0
- pyinfra/operations/dnf.py +210 -0
- pyinfra/operations/docker.py +446 -0
- pyinfra/operations/files.py +1939 -0
- pyinfra/operations/flatpak.py +94 -0
- pyinfra/operations/freebsd/__init__.py +12 -0
- pyinfra/operations/freebsd/freebsd_update.py +70 -0
- pyinfra/operations/freebsd/pkg.py +219 -0
- pyinfra/operations/freebsd/service.py +116 -0
- pyinfra/operations/freebsd/sysrc.py +92 -0
- pyinfra/operations/gem.py +47 -0
- pyinfra/operations/git.py +419 -0
- pyinfra/operations/iptables.py +311 -0
- pyinfra/operations/launchd.py +45 -0
- pyinfra/operations/lxd.py +68 -0
- pyinfra/operations/mysql.py +609 -0
- pyinfra/operations/npm.py +57 -0
- pyinfra/operations/openrc.py +63 -0
- pyinfra/operations/opkg.py +88 -0
- pyinfra/operations/pacman.py +81 -0
- pyinfra/operations/pip.py +205 -0
- pyinfra/operations/pipx.py +102 -0
- pyinfra/operations/pkg.py +70 -0
- pyinfra/operations/pkgin.py +91 -0
- pyinfra/operations/postgres.py +436 -0
- pyinfra/operations/postgresql.py +30 -0
- pyinfra/operations/puppet.py +40 -0
- pyinfra/operations/python.py +72 -0
- pyinfra/operations/runit.py +184 -0
- pyinfra/operations/selinux.py +189 -0
- pyinfra/operations/server.py +1099 -0
- pyinfra/operations/snap.py +117 -0
- pyinfra/operations/ssh.py +216 -0
- pyinfra/operations/systemd.py +149 -0
- pyinfra/operations/sysvinit.py +141 -0
- pyinfra/operations/upstart.py +68 -0
- pyinfra/operations/util/__init__.py +12 -0
- pyinfra/operations/util/docker.py +251 -0
- pyinfra/operations/util/files.py +247 -0
- pyinfra/operations/util/packaging.py +336 -0
- pyinfra/operations/util/service.py +46 -0
- pyinfra/operations/vzctl.py +137 -0
- pyinfra/operations/xbps.py +77 -0
- pyinfra/operations/yum.py +210 -0
- pyinfra/operations/zfs.py +175 -0
- pyinfra/operations/zypper.py +192 -0
- pyinfra/progress.py +44 -32
- pyinfra/py.typed +0 -0
- pyinfra/version.py +9 -1
- pyinfra-3.5.1.dist-info/METADATA +141 -0
- pyinfra-3.5.1.dist-info/RECORD +159 -0
- {pyinfra-0.11.dev3.dist-info → pyinfra-3.5.1.dist-info}/WHEEL +1 -2
- pyinfra-3.5.1.dist-info/entry_points.txt +12 -0
- {pyinfra-0.11.dev3.dist-info → pyinfra-3.5.1.dist-info/licenses}/LICENSE.md +1 -1
- pyinfra_cli/__init__.py +1 -0
- pyinfra_cli/cli.py +780 -0
- pyinfra_cli/commands.py +66 -0
- pyinfra_cli/exceptions.py +155 -65
- pyinfra_cli/inventory.py +233 -89
- pyinfra_cli/log.py +39 -43
- pyinfra_cli/main.py +26 -495
- pyinfra_cli/prints.py +215 -156
- pyinfra_cli/util.py +172 -105
- pyinfra_cli/virtualenv.py +25 -20
- pyinfra/api/connectors/__init__.py +0 -21
- pyinfra/api/connectors/ansible.py +0 -99
- pyinfra/api/connectors/docker.py +0 -178
- pyinfra/api/connectors/local.py +0 -169
- pyinfra/api/connectors/ssh.py +0 -402
- pyinfra/api/connectors/sshuserclient/client.py +0 -105
- pyinfra/api/connectors/sshuserclient/config.py +0 -90
- pyinfra/api/connectors/util.py +0 -63
- pyinfra/api/connectors/vagrant.py +0 -155
- pyinfra/facts/init.py +0 -176
- pyinfra/facts/util/files.py +0 -102
- pyinfra/hook.py +0 -41
- pyinfra/modules/__init__.py +0 -11
- pyinfra/modules/apk.py +0 -64
- pyinfra/modules/apt.py +0 -272
- pyinfra/modules/brew.py +0 -122
- pyinfra/modules/files.py +0 -711
- pyinfra/modules/gem.py +0 -30
- pyinfra/modules/git.py +0 -115
- pyinfra/modules/init.py +0 -344
- pyinfra/modules/iptables.py +0 -271
- pyinfra/modules/lxd.py +0 -45
- pyinfra/modules/mysql.py +0 -347
- pyinfra/modules/npm.py +0 -47
- pyinfra/modules/pacman.py +0 -60
- pyinfra/modules/pip.py +0 -99
- pyinfra/modules/pkg.py +0 -43
- pyinfra/modules/postgresql.py +0 -245
- pyinfra/modules/puppet.py +0 -20
- pyinfra/modules/python.py +0 -37
- pyinfra/modules/server.py +0 -524
- pyinfra/modules/ssh.py +0 -150
- pyinfra/modules/util/files.py +0 -52
- pyinfra/modules/util/packaging.py +0 -118
- pyinfra/modules/vzctl.py +0 -133
- pyinfra/modules/yum.py +0 -171
- pyinfra/pseudo_modules.py +0 -64
- pyinfra-0.11.dev3.dist-info/.DS_Store +0 -0
- pyinfra-0.11.dev3.dist-info/METADATA +0 -135
- pyinfra-0.11.dev3.dist-info/RECORD +0 -95
- pyinfra-0.11.dev3.dist-info/entry_points.txt +0 -3
- pyinfra-0.11.dev3.dist-info/top_level.txt +0 -2
- pyinfra_cli/__main__.py +0 -40
- pyinfra_cli/config.py +0 -92
- /pyinfra/{modules/util → connectors}/__init__.py +0 -0
- /pyinfra/{api/connectors → connectors}/sshuserclient/__init__.py +0 -0
pyinfra/facts/iptables.py
CHANGED
|
@@ -1,56 +1,53 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing_extensions import override
|
|
4
|
+
|
|
1
5
|
from pyinfra.api import FactBase
|
|
2
6
|
|
|
3
7
|
# Mapping for iptables code arguments to variable names
|
|
4
8
|
IPTABLES_ARGS = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
"-A": "chain",
|
|
10
|
+
"-j": "jump",
|
|
8
11
|
# Boolean matches
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
"-p": "protocol",
|
|
13
|
+
"-s": "source",
|
|
14
|
+
"-d": "destination",
|
|
15
|
+
"-i": "in_interface",
|
|
16
|
+
"-o": "out_interface",
|
|
15
17
|
# Logging
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
"--log-prefix": "log_prefix",
|
|
18
19
|
# NAT exit rules
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
"--to-destination": "to_destination",
|
|
21
|
+
"--to-source": "to_source",
|
|
22
|
+
"--to-ports": "to_ports",
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
def parse_iptables_rule(line):
|
|
26
|
-
|
|
27
|
+
"""
|
|
27
28
|
Parse one iptables rule. Returns a dict where each iptables code argument
|
|
28
29
|
is mapped to a name using IPTABLES_ARGS.
|
|
29
|
-
|
|
30
|
+
"""
|
|
30
31
|
|
|
31
32
|
bits = line.split()
|
|
32
33
|
|
|
33
|
-
definition = {}
|
|
34
|
+
definition: dict = {}
|
|
34
35
|
|
|
35
36
|
key = None
|
|
36
|
-
args = []
|
|
37
|
+
args: list[str] = []
|
|
37
38
|
not_arg = False
|
|
38
39
|
|
|
39
|
-
def add_args():
|
|
40
|
-
arg_string =
|
|
40
|
+
def add_args() -> None:
|
|
41
|
+
arg_string = " ".join(args)
|
|
41
42
|
|
|
42
|
-
if key in IPTABLES_ARGS:
|
|
43
|
-
definition_key = (
|
|
44
|
-
'not_{0}'.format(IPTABLES_ARGS[key])
|
|
45
|
-
if not_arg
|
|
46
|
-
else IPTABLES_ARGS[key]
|
|
47
|
-
)
|
|
43
|
+
if key and key in IPTABLES_ARGS:
|
|
44
|
+
definition_key = "not_{0}".format(IPTABLES_ARGS[key]) if not_arg else IPTABLES_ARGS[key]
|
|
48
45
|
definition[definition_key] = arg_string
|
|
49
46
|
else:
|
|
50
|
-
definition.setdefault(
|
|
47
|
+
definition.setdefault("extras", []).extend((key, arg_string))
|
|
51
48
|
|
|
52
49
|
for bit in bits:
|
|
53
|
-
if bit ==
|
|
50
|
+
if bit == "!":
|
|
54
51
|
if key:
|
|
55
52
|
add_args()
|
|
56
53
|
args = []
|
|
@@ -58,7 +55,7 @@ def parse_iptables_rule(line):
|
|
|
58
55
|
|
|
59
56
|
not_arg = True
|
|
60
57
|
|
|
61
|
-
elif bit.startswith(
|
|
58
|
+
elif bit.startswith("-"):
|
|
62
59
|
if key:
|
|
63
60
|
add_args()
|
|
64
61
|
args = []
|
|
@@ -72,74 +69,85 @@ def parse_iptables_rule(line):
|
|
|
72
69
|
if key:
|
|
73
70
|
add_args()
|
|
74
71
|
|
|
75
|
-
if
|
|
76
|
-
definition[
|
|
72
|
+
if "extras" in definition:
|
|
73
|
+
definition["extras"] = set(definition["extras"])
|
|
77
74
|
|
|
78
75
|
return definition
|
|
79
76
|
|
|
80
77
|
|
|
81
78
|
class IptablesRules(FactBase):
|
|
82
|
-
|
|
79
|
+
"""
|
|
83
80
|
Returns a list of iptables rules for a specific table:
|
|
84
81
|
|
|
85
82
|
.. code:: python
|
|
86
83
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
84
|
+
[
|
|
85
|
+
{
|
|
86
|
+
"chain": "PREROUTING",
|
|
87
|
+
"jump": "DNAT",
|
|
88
|
+
},
|
|
89
|
+
]
|
|
90
|
+
"""
|
|
93
91
|
|
|
94
92
|
default = list
|
|
95
93
|
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
@override
|
|
95
|
+
def command(self, table="filter"):
|
|
96
|
+
return "iptables-save -t {0}".format(table)
|
|
98
97
|
|
|
98
|
+
@override
|
|
99
99
|
def process(self, output):
|
|
100
100
|
rules = []
|
|
101
101
|
|
|
102
102
|
for line in output:
|
|
103
|
-
if line.startswith(
|
|
103
|
+
if line.startswith("-"):
|
|
104
104
|
rules.append(parse_iptables_rule(line))
|
|
105
105
|
|
|
106
106
|
return rules
|
|
107
107
|
|
|
108
108
|
|
|
109
109
|
class Ip6tablesRules(IptablesRules):
|
|
110
|
-
|
|
110
|
+
"""
|
|
111
111
|
Returns a list of ip6tables rules for a specific table:
|
|
112
112
|
|
|
113
113
|
.. code:: python
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
115
|
+
[
|
|
116
|
+
{
|
|
117
|
+
"chain": "PREROUTING",
|
|
118
|
+
"jump": "DNAT",
|
|
119
|
+
},
|
|
120
|
+
]
|
|
121
|
+
"""
|
|
121
122
|
|
|
122
|
-
|
|
123
|
-
|
|
123
|
+
@override
|
|
124
|
+
def command(self, table="filter"):
|
|
125
|
+
return "ip6tables-save -t {0}".format(table)
|
|
124
126
|
|
|
125
127
|
|
|
126
|
-
class IptablesChains(
|
|
127
|
-
|
|
128
|
+
class IptablesChains(FactBase):
|
|
129
|
+
"""
|
|
128
130
|
Returns a dict of iptables chains & policies:
|
|
129
131
|
|
|
130
132
|
.. code:: python
|
|
131
133
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
134
|
+
{
|
|
135
|
+
"NAME": "POLICY",
|
|
136
|
+
}
|
|
137
|
+
"""
|
|
135
138
|
|
|
136
139
|
default = dict
|
|
137
140
|
|
|
141
|
+
@override
|
|
142
|
+
def command(self, table="filter"):
|
|
143
|
+
return "iptables-save -t {0}".format(table)
|
|
144
|
+
|
|
145
|
+
@override
|
|
138
146
|
def process(self, output):
|
|
139
147
|
chains = {}
|
|
140
148
|
|
|
141
149
|
for line in output:
|
|
142
|
-
if line.startswith(
|
|
150
|
+
if line.startswith(":"):
|
|
143
151
|
line = line[1:]
|
|
144
152
|
|
|
145
153
|
name, policy, _ = line.split()
|
|
@@ -149,14 +157,16 @@ class IptablesChains(IptablesRules):
|
|
|
149
157
|
|
|
150
158
|
|
|
151
159
|
class Ip6tablesChains(IptablesChains):
|
|
152
|
-
|
|
160
|
+
"""
|
|
153
161
|
Returns a dict of ip6tables chains & policies:
|
|
154
162
|
|
|
155
163
|
.. code:: python
|
|
156
164
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
165
|
+
{
|
|
166
|
+
"NAME": "POLICY",
|
|
167
|
+
}
|
|
168
|
+
"""
|
|
160
169
|
|
|
161
|
-
|
|
162
|
-
|
|
170
|
+
@override
|
|
171
|
+
def command(self, table="filter"):
|
|
172
|
+
return "ip6tables-save -t {0}".format(table)
|
pyinfra/facts/launchd.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing_extensions import override
|
|
4
|
+
|
|
5
|
+
from pyinfra.api import FactBase
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LaunchdStatus(FactBase):
|
|
9
|
+
"""
|
|
10
|
+
Returns a dict of name -> status for launchd managed services.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
@override
|
|
14
|
+
def command(self) -> str:
|
|
15
|
+
return "launchctl list"
|
|
16
|
+
|
|
17
|
+
@override
|
|
18
|
+
def requires_command(self) -> str:
|
|
19
|
+
return "launchctl"
|
|
20
|
+
|
|
21
|
+
default = dict
|
|
22
|
+
|
|
23
|
+
@override
|
|
24
|
+
def process(self, output):
|
|
25
|
+
services = {}
|
|
26
|
+
|
|
27
|
+
for line in output:
|
|
28
|
+
bits = line.split()
|
|
29
|
+
|
|
30
|
+
if not bits or bits[0] == "PID":
|
|
31
|
+
continue
|
|
32
|
+
|
|
33
|
+
name = bits[2]
|
|
34
|
+
status = False
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
int(bits[0])
|
|
38
|
+
status = True
|
|
39
|
+
except ValueError:
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
services[name] = status
|
|
43
|
+
|
|
44
|
+
return services
|
pyinfra/facts/lxd.py
CHANGED
|
@@ -1,16 +1,29 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import json
|
|
2
4
|
|
|
5
|
+
from typing_extensions import override
|
|
6
|
+
|
|
3
7
|
from pyinfra.api import FactBase
|
|
4
8
|
|
|
5
9
|
|
|
6
|
-
class
|
|
7
|
-
|
|
10
|
+
class LxdContainers(FactBase):
|
|
11
|
+
"""
|
|
8
12
|
Returns a list of running LXD containers
|
|
9
|
-
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
@override
|
|
16
|
+
def command(self) -> str:
|
|
17
|
+
return "lxc list --format json --fast"
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def requires_command(self) -> str:
|
|
21
|
+
return "lxc"
|
|
10
22
|
|
|
11
|
-
command = 'lxc list --format json --fast'
|
|
12
23
|
default = list
|
|
13
24
|
|
|
25
|
+
@override
|
|
14
26
|
def process(self, output):
|
|
27
|
+
output = list(output)
|
|
15
28
|
assert len(output) == 1
|
|
16
29
|
return json.loads(output[0])
|
pyinfra/facts/mysql.py
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
import
|
|
1
|
+
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import re
|
|
3
4
|
from collections import defaultdict
|
|
4
5
|
|
|
5
|
-
from
|
|
6
|
+
from typing_extensions import override
|
|
7
|
+
|
|
8
|
+
from pyinfra.api import FactBase, MaskString, QuoteString, StringCommand
|
|
6
9
|
from pyinfra.api.util import try_int
|
|
7
10
|
|
|
8
11
|
from .util.databases import parse_columns_and_rows
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
def make_mysql_command(
|
|
12
|
-
database=None,
|
|
13
|
-
user=None,
|
|
14
|
-
password=None,
|
|
15
|
-
host=None,
|
|
16
|
-
port=None,
|
|
17
|
-
executable=
|
|
15
|
+
database: str | None = None,
|
|
16
|
+
user: str | None = None,
|
|
17
|
+
password: str | None = None,
|
|
18
|
+
host: str | None = None,
|
|
19
|
+
port: int | None = None,
|
|
20
|
+
executable="mysql",
|
|
18
21
|
):
|
|
19
22
|
target_bits = [executable]
|
|
20
23
|
|
|
@@ -27,35 +30,56 @@ def make_mysql_command(
|
|
|
27
30
|
|
|
28
31
|
if password:
|
|
29
32
|
# Quote the password as it may contain special characters
|
|
30
|
-
target_bits.append('-p"{0}"'.format(password))
|
|
33
|
+
target_bits.append(MaskString('-p"{0}"'.format(password)))
|
|
31
34
|
|
|
32
35
|
if host:
|
|
33
|
-
target_bits.append(
|
|
36
|
+
target_bits.append("-h{0}".format(host))
|
|
34
37
|
|
|
35
38
|
if port:
|
|
36
|
-
target_bits.append(
|
|
39
|
+
target_bits.append("-P{0}".format(port))
|
|
37
40
|
|
|
38
|
-
return
|
|
41
|
+
return StringCommand(*target_bits)
|
|
39
42
|
|
|
40
43
|
|
|
41
|
-
def make_execute_mysql_command(
|
|
42
|
-
|
|
44
|
+
def make_execute_mysql_command(
|
|
45
|
+
command: str | StringCommand,
|
|
46
|
+
ignore_errors=False,
|
|
47
|
+
**mysql_kwargs,
|
|
48
|
+
):
|
|
49
|
+
commands_bits = [
|
|
43
50
|
make_mysql_command(**mysql_kwargs),
|
|
44
|
-
|
|
45
|
-
|
|
51
|
+
"-Be",
|
|
52
|
+
QuoteString(command), # quote this whole item as a single shell argument
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
if ignore_errors:
|
|
56
|
+
commands_bits.extend(["||", "true"])
|
|
57
|
+
|
|
58
|
+
return StringCommand(*commands_bits)
|
|
46
59
|
|
|
47
60
|
|
|
48
61
|
class MysqlFactBase(FactBase):
|
|
49
62
|
abstract = True
|
|
50
63
|
|
|
64
|
+
mysql_command: str
|
|
65
|
+
ignore_errors = False
|
|
66
|
+
|
|
67
|
+
@override
|
|
68
|
+
def requires_command(self, *args, **kwargs) -> str:
|
|
69
|
+
return "mysql"
|
|
70
|
+
|
|
71
|
+
@override
|
|
51
72
|
def command(
|
|
52
73
|
self,
|
|
53
74
|
# Details for speaking to MySQL via `mysql` CLI via `mysql` CLI
|
|
54
|
-
mysql_user=None,
|
|
55
|
-
|
|
56
|
-
|
|
75
|
+
mysql_user=None,
|
|
76
|
+
mysql_password=None,
|
|
77
|
+
mysql_host=None,
|
|
78
|
+
mysql_port=None,
|
|
79
|
+
) -> StringCommand:
|
|
57
80
|
return make_execute_mysql_command(
|
|
58
81
|
self.mysql_command,
|
|
82
|
+
ignore_errors=self.ignore_errors,
|
|
59
83
|
user=mysql_user,
|
|
60
84
|
password=mysql_password,
|
|
61
85
|
host=mysql_host,
|
|
@@ -64,130 +88,142 @@ class MysqlFactBase(FactBase):
|
|
|
64
88
|
|
|
65
89
|
|
|
66
90
|
class MysqlDatabases(MysqlFactBase):
|
|
67
|
-
|
|
91
|
+
"""
|
|
68
92
|
Returns a dict of existing MySQL databases and associated data:
|
|
69
93
|
|
|
70
94
|
.. code:: python
|
|
71
95
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
96
|
+
{
|
|
97
|
+
"mysql": {
|
|
98
|
+
"character_set": "latin1",
|
|
99
|
+
"collation_name": "latin1_swedish_ci"
|
|
100
|
+
},
|
|
101
|
+
}
|
|
102
|
+
"""
|
|
78
103
|
|
|
79
104
|
default = dict
|
|
80
|
-
mysql_command =
|
|
105
|
+
mysql_command = "SELECT * FROM information_schema.SCHEMATA"
|
|
81
106
|
|
|
107
|
+
@override
|
|
82
108
|
def process(self, output):
|
|
83
109
|
rows = parse_columns_and_rows(
|
|
84
|
-
output,
|
|
110
|
+
output,
|
|
111
|
+
"\t",
|
|
85
112
|
title_parser=lambda title: title.lower(),
|
|
86
113
|
)
|
|
87
114
|
|
|
88
115
|
databases = {}
|
|
89
116
|
|
|
90
117
|
for details in rows:
|
|
91
|
-
databases[details.pop(
|
|
92
|
-
|
|
93
|
-
|
|
118
|
+
databases[details.pop("schema_name")] = {
|
|
119
|
+
"character_set": details["default_character_set_name"],
|
|
120
|
+
"collation_name": details["default_collation_name"],
|
|
94
121
|
}
|
|
95
122
|
|
|
96
123
|
return databases
|
|
97
124
|
|
|
98
125
|
|
|
99
126
|
class MysqlUsers(MysqlFactBase):
|
|
100
|
-
|
|
127
|
+
"""
|
|
101
128
|
Returns a dict of MySQL ``user@host``'s and their associated data:
|
|
102
129
|
|
|
103
130
|
.. code:: python
|
|
104
131
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
132
|
+
{
|
|
133
|
+
"user@host": {
|
|
134
|
+
"privileges": ["Alter", "Grant"],
|
|
135
|
+
'max_connections': 5,
|
|
136
|
+
...
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
"""
|
|
112
140
|
|
|
113
141
|
default = dict
|
|
114
|
-
mysql_command =
|
|
142
|
+
mysql_command = "SELECT * FROM mysql.user"
|
|
115
143
|
|
|
116
|
-
@
|
|
117
|
-
def process(output):
|
|
118
|
-
rows = parse_columns_and_rows(output,
|
|
144
|
+
@override
|
|
145
|
+
def process(self, output):
|
|
146
|
+
rows = parse_columns_and_rows(output, "\t")
|
|
119
147
|
|
|
120
148
|
users = {}
|
|
121
149
|
|
|
122
150
|
for details in rows:
|
|
123
|
-
if details.get(
|
|
151
|
+
if details.get("Host") is None or details.get("User") is None:
|
|
124
152
|
continue # pragma: no cover
|
|
125
153
|
|
|
126
154
|
privileges = []
|
|
127
155
|
|
|
128
156
|
for key, value in list(details.items()):
|
|
129
|
-
if key.endswith(
|
|
130
|
-
privileges.append(key.replace(
|
|
157
|
+
if key.endswith("_priv") and details.pop(key) == "Y":
|
|
158
|
+
privileges.append(key.replace("_priv", ""))
|
|
131
159
|
|
|
132
|
-
if key.startswith(
|
|
160
|
+
if key.startswith("max_"):
|
|
133
161
|
details[key] = try_int(value)
|
|
134
162
|
|
|
135
|
-
if key in (
|
|
136
|
-
details[key] = value ==
|
|
163
|
+
if key in ("password_expired", "is_role"):
|
|
164
|
+
details[key] = value == "Y"
|
|
137
165
|
|
|
138
|
-
details[
|
|
166
|
+
details["privileges"] = sorted(privileges)
|
|
139
167
|
|
|
140
168
|
# Attach the user in the format user@host
|
|
141
|
-
users[
|
|
142
|
-
|
|
143
|
-
|
|
169
|
+
users[
|
|
170
|
+
"{0}@{1}".format(
|
|
171
|
+
details.pop("User"),
|
|
172
|
+
details.pop("Host"),
|
|
173
|
+
)
|
|
174
|
+
] = details
|
|
144
175
|
|
|
145
176
|
return users
|
|
146
177
|
|
|
147
178
|
|
|
148
|
-
MYSQL_GRANT_REGEX =
|
|
149
|
-
r"^GRANT ([A-Z,\s]+) ON (
|
|
179
|
+
MYSQL_GRANT_REGEX = (
|
|
180
|
+
r"^GRANT ([A-Z,\s]+) ON ((?:\*|`[a-z_\\]+`)\.(?:\*|`[a-z_]+`)) "
|
|
181
|
+
r"TO `[A-Z0-9a-z_\-]+`@`(?:%|[A-Z0-9a-z_\.\-]+)`(.*)"
|
|
150
182
|
)
|
|
151
183
|
|
|
152
184
|
|
|
153
185
|
class MysqlUserGrants(MysqlFactBase):
|
|
154
|
-
|
|
155
|
-
Returns a dict of ``<database>`.<table>`` with granted privileges for each:
|
|
186
|
+
"""
|
|
187
|
+
Returns a dict of ``<database>`.<table>`` with a set of granted privileges for each:
|
|
156
188
|
|
|
157
189
|
.. code:: python
|
|
158
190
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
'''
|
|
191
|
+
{
|
|
192
|
+
"`pyinfra_stuff`.*": {
|
|
193
|
+
"SELECT",
|
|
194
|
+
"INSERT",
|
|
195
|
+
"GRANT OPTION",
|
|
196
|
+
},
|
|
197
|
+
}
|
|
198
|
+
"""
|
|
168
199
|
|
|
169
200
|
default = dict
|
|
201
|
+
# Ignore errors as SHOW GRANTS will error if the user does not exist
|
|
202
|
+
ignore_errors = True
|
|
170
203
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
204
|
+
@override
|
|
205
|
+
def command( # type: ignore[override]
|
|
206
|
+
self,
|
|
207
|
+
user,
|
|
208
|
+
hostname="localhost",
|
|
174
209
|
# Details for speaking to MySQL via `mysql` CLI via `mysql` CLI
|
|
175
|
-
mysql_user=None,
|
|
176
|
-
|
|
177
|
-
|
|
210
|
+
mysql_user=None,
|
|
211
|
+
mysql_password=None,
|
|
212
|
+
mysql_host=None,
|
|
213
|
+
mysql_port=None,
|
|
214
|
+
) -> StringCommand:
|
|
178
215
|
self.mysql_command = 'SHOW GRANTS FOR "{0}"@"{1}"'.format(user, hostname)
|
|
179
216
|
|
|
180
|
-
return super(
|
|
181
|
-
mysql_user,
|
|
182
|
-
|
|
217
|
+
return super().command(
|
|
218
|
+
mysql_user,
|
|
219
|
+
mysql_password,
|
|
220
|
+
mysql_host,
|
|
221
|
+
mysql_port,
|
|
183
222
|
)
|
|
184
223
|
|
|
185
|
-
@
|
|
186
|
-
def process(output):
|
|
187
|
-
database_table_privileges = defaultdict(
|
|
188
|
-
'privileges': set(),
|
|
189
|
-
'with_grant_option': False,
|
|
190
|
-
})
|
|
224
|
+
@override
|
|
225
|
+
def process(self, output):
|
|
226
|
+
database_table_privileges = defaultdict(set)
|
|
191
227
|
|
|
192
228
|
for line in output:
|
|
193
229
|
matches = re.match(MYSQL_GRANT_REGEX, line)
|
|
@@ -197,13 +233,13 @@ class MysqlUserGrants(MysqlFactBase):
|
|
|
197
233
|
privileges, database_table, extras = matches.groups()
|
|
198
234
|
|
|
199
235
|
# MySQL outputs this pre-escaped
|
|
200
|
-
database_table = database_table.replace(
|
|
236
|
+
database_table = database_table.replace("\\\\", "\\")
|
|
201
237
|
|
|
202
|
-
for privilege in privileges.split(
|
|
238
|
+
for privilege in privileges.split(","):
|
|
203
239
|
privilege = privilege.strip()
|
|
204
|
-
database_table_privileges[database_table]
|
|
240
|
+
database_table_privileges[database_table].add(privilege)
|
|
205
241
|
|
|
206
|
-
if
|
|
207
|
-
database_table_privileges[database_table]
|
|
242
|
+
if "WITH GRANT OPTION" in extras:
|
|
243
|
+
database_table_privileges[database_table].add("GRANT OPTION")
|
|
208
244
|
|
|
209
245
|
return database_table_privileges
|
pyinfra/facts/npm.py
CHANGED
|
@@ -1,30 +1,38 @@
|
|
|
1
1
|
# encoding: utf8
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing_extensions import override
|
|
2
5
|
|
|
3
6
|
from pyinfra.api import FactBase
|
|
4
7
|
|
|
5
8
|
from .util.packaging import parse_packages
|
|
6
9
|
|
|
7
|
-
NPM_REGEX = r
|
|
10
|
+
NPM_REGEX = r"^[└├]\─\─\s([a-zA-Z0-9\-]+)@([0-9\.]+)$"
|
|
8
11
|
|
|
9
12
|
|
|
10
13
|
class NpmPackages(FactBase):
|
|
11
|
-
|
|
14
|
+
"""
|
|
12
15
|
Returns a dict of installed npm packages globally or in a given directory:
|
|
13
16
|
|
|
14
17
|
.. code:: python
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
{
|
|
20
|
+
"package_name": ["version"],
|
|
21
|
+
}
|
|
22
|
+
"""
|
|
19
23
|
|
|
20
|
-
command = 'npm list -g --depth=0'
|
|
21
24
|
default = dict
|
|
22
25
|
|
|
26
|
+
@override
|
|
27
|
+
def requires_command(self, directory=None) -> str:
|
|
28
|
+
return "npm"
|
|
29
|
+
|
|
30
|
+
@override
|
|
23
31
|
def command(self, directory=None):
|
|
24
32
|
if directory:
|
|
25
|
-
return
|
|
26
|
-
|
|
27
|
-
return 'npm list -g --depth=0'
|
|
33
|
+
return ("cd {0} && npm list -g --depth=0").format(directory)
|
|
34
|
+
return "npm list -g --depth=0"
|
|
28
35
|
|
|
36
|
+
@override
|
|
29
37
|
def process(self, output):
|
|
30
38
|
return parse_packages(NPM_REGEX, output)
|