saltext-opnsense 0.1.1.dev0__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.
- saltext/opnsense/__init__.py +6 -0
- saltext/opnsense/grains/opnsense.py +46 -0
- saltext/opnsense/modules/acmeclient.py +171 -0
- saltext/opnsense/modules/bind.py +229 -0
- saltext/opnsense/modules/dns.py +181 -0
- saltext/opnsense/modules/firewall.py +105 -0
- saltext/opnsense/modules/kea.py +206 -0
- saltext/opnsense/modules/opnsense.py +531 -0
- saltext/opnsense/modules/unbound.py +308 -0
- saltext/opnsense/proxy/opnsense.py +164 -0
- saltext/opnsense/states/bind.py +460 -0
- saltext/opnsense/states/dns.py +352 -0
- saltext/opnsense/states/opnsense.py +1986 -0
- saltext/opnsense/states/unbound.py +539 -0
- saltext/opnsense/utils/api_spec.py +158 -0
- saltext/opnsense/utils/common.py +99 -0
- saltext/opnsense/utils/controllers.json +4982 -0
- saltext/opnsense/utils/models.json +16010 -0
- saltext/opnsense/utils/models.py +239 -0
- saltext/opnsense/utils/opnsense.py +658 -0
- saltext/opnsense/version/__init__.py +10 -0
- saltext/opnsense/version/_version.py +24 -0
- saltext_opnsense-0.1.1.dev0.dist-info/METADATA +137 -0
- saltext_opnsense-0.1.1.dev0.dist-info/RECORD +28 -0
- saltext_opnsense-0.1.1.dev0.dist-info/WHEEL +5 -0
- saltext_opnsense-0.1.1.dev0.dist-info/entry_points.txt +2 -0
- saltext_opnsense-0.1.1.dev0.dist-info/licenses/LICENSE +21 -0
- saltext_opnsense-0.1.1.dev0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
log = logging.getLogger(__name__)
|
|
4
|
+
|
|
5
|
+
__virtualname__ = "opnsense"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def __virtual__():
|
|
9
|
+
if "__proxy__" not in globals():
|
|
10
|
+
return (False, "opnsense grains only run on proxy minions")
|
|
11
|
+
return __virtualname__
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def opnsense_grains():
|
|
15
|
+
grains = {}
|
|
16
|
+
try:
|
|
17
|
+
if "__salt__" in globals() and "opnsense.ping" in __salt__:
|
|
18
|
+
try:
|
|
19
|
+
if not __salt__["opnsense.ping"]():
|
|
20
|
+
return grains
|
|
21
|
+
except Exception:
|
|
22
|
+
pass
|
|
23
|
+
try:
|
|
24
|
+
spec = __salt__["opnsense.spec"]()
|
|
25
|
+
if isinstance(spec, dict):
|
|
26
|
+
mods = spec.get("modules", {})
|
|
27
|
+
meta = spec.get("meta", {})
|
|
28
|
+
grains["opnsense_api_modules"] = list(mods.keys()) if isinstance(mods, dict) else list(mods)
|
|
29
|
+
grains["opnsense_spec_version"] = meta.get("core_ref", "25.7")
|
|
30
|
+
except Exception:
|
|
31
|
+
pass
|
|
32
|
+
try:
|
|
33
|
+
fw_status = __salt__["opnsense.call"]("core", "firmware", "status", data={}, method="POST")
|
|
34
|
+
if isinstance(fw_status, dict):
|
|
35
|
+
grains["opnsense_version"] = fw_status.get("product_version") or fw_status.get("product_version_string")
|
|
36
|
+
except Exception:
|
|
37
|
+
pass
|
|
38
|
+
try:
|
|
39
|
+
alias_res = __salt__["opnsense.search"]("unbound", "settings", "host_alias", row_count=1)
|
|
40
|
+
if isinstance(alias_res, dict):
|
|
41
|
+
grains["opnsense_unbound_alias_count"] = alias_res.get("total", 0)
|
|
42
|
+
except Exception:
|
|
43
|
+
pass
|
|
44
|
+
except Exception as exc:
|
|
45
|
+
log.debug("opnsense grains failed: %s", exc)
|
|
46
|
+
return grains
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
log = logging.getLogger(__name__)
|
|
4
|
+
|
|
5
|
+
__virtualname__ = "opnsense_acmeclient"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def __virtual__():
|
|
9
|
+
if "opnsense.search" in __salt__ or "opnsense.call" in __salt__:
|
|
10
|
+
return __virtualname__
|
|
11
|
+
return (False, "opnsense execution module not loaded")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _search(controller, type_name, search_phrase="", row_count=-1):
|
|
15
|
+
try:
|
|
16
|
+
fn = __salt__["opnsense.search"]
|
|
17
|
+
res = fn("acmeclient", controller, type_name, search_phrase=search_phrase, row_count=row_count)
|
|
18
|
+
if isinstance(res, dict):
|
|
19
|
+
return res.get("rows", [])
|
|
20
|
+
return []
|
|
21
|
+
except Exception as exc:
|
|
22
|
+
log.debug("acmeclient search %s/%s failed: %s", controller, type_name, exc)
|
|
23
|
+
return []
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _sorted_dict(d):
|
|
27
|
+
return dict(sorted(d.items()))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def list_accounts():
|
|
31
|
+
rows = _search("accounts", "account", row_count=-1)
|
|
32
|
+
result = {}
|
|
33
|
+
for r in rows:
|
|
34
|
+
name = r.get("name") or r.get("uuid") or ""
|
|
35
|
+
if not name:
|
|
36
|
+
continue
|
|
37
|
+
result[name] = {
|
|
38
|
+
"name": name,
|
|
39
|
+
"uuid": r.get("uuid"),
|
|
40
|
+
"email": r.get("email") or r.get("contact") or "",
|
|
41
|
+
"ca": r.get("ca") or r.get("directoryUrl") or "",
|
|
42
|
+
"enabled": r.get("enabled") in ("1", True, 1, None),
|
|
43
|
+
"raw": r,
|
|
44
|
+
}
|
|
45
|
+
return _sorted_dict(result)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def list_accounts_pretty():
|
|
49
|
+
data = list_accounts()
|
|
50
|
+
lines = []
|
|
51
|
+
for name in sorted(data):
|
|
52
|
+
info = data[name]
|
|
53
|
+
lines.append(f"{name} [{info.get('ca')}] uuid={info.get('uuid','')[:8]}")
|
|
54
|
+
return lines
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def list_validations():
|
|
58
|
+
rows = _search("validations", "validation", row_count=-1)
|
|
59
|
+
result = {}
|
|
60
|
+
for r in rows:
|
|
61
|
+
name = r.get("name") or r.get("uuid") or ""
|
|
62
|
+
if not name:
|
|
63
|
+
continue
|
|
64
|
+
result[name] = {
|
|
65
|
+
"name": name,
|
|
66
|
+
"uuid": r.get("uuid"),
|
|
67
|
+
"method": r.get("method") or r.get("validation_type") or "",
|
|
68
|
+
"enabled": r.get("enabled") in ("1", True, 1, None),
|
|
69
|
+
"raw": r,
|
|
70
|
+
}
|
|
71
|
+
return _sorted_dict(result)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def list_validations_pretty():
|
|
75
|
+
data = list_validations()
|
|
76
|
+
lines = []
|
|
77
|
+
for name in sorted(data):
|
|
78
|
+
info = data[name]
|
|
79
|
+
lines.append(f"{name} method={info.get('method')} uuid={info.get('uuid','')[:8]}")
|
|
80
|
+
return lines
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def list_actions():
|
|
84
|
+
rows = _search("actions", "action", row_count=-1)
|
|
85
|
+
result = {}
|
|
86
|
+
for r in rows:
|
|
87
|
+
name = r.get("name") or r.get("uuid") or ""
|
|
88
|
+
if not name:
|
|
89
|
+
continue
|
|
90
|
+
result[name] = {
|
|
91
|
+
"name": name,
|
|
92
|
+
"uuid": r.get("uuid"),
|
|
93
|
+
"type": r.get("type") or r.get("action_type") or "",
|
|
94
|
+
"enabled": r.get("enabled") in ("1", True, 1, None),
|
|
95
|
+
"raw": r,
|
|
96
|
+
}
|
|
97
|
+
return _sorted_dict(result)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def list_actions_pretty():
|
|
101
|
+
data = list_actions()
|
|
102
|
+
lines = []
|
|
103
|
+
for name in sorted(data):
|
|
104
|
+
info = data[name]
|
|
105
|
+
lines.append(f"{name} type={info.get('type')} uuid={info.get('uuid','')[:8]}")
|
|
106
|
+
return lines
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def list_certificates():
|
|
110
|
+
rows = _search("certificates", "certificate", row_count=-1)
|
|
111
|
+
result = {}
|
|
112
|
+
for r in rows:
|
|
113
|
+
name = r.get("name") or r.get("common_name") or r.get("uuid") or ""
|
|
114
|
+
if not name:
|
|
115
|
+
continue
|
|
116
|
+
result[name] = {
|
|
117
|
+
"name": name,
|
|
118
|
+
"uuid": r.get("uuid"),
|
|
119
|
+
"common_name": r.get("common_name") or name,
|
|
120
|
+
"status": r.get("status") or r.get("last_status") or "",
|
|
121
|
+
"account": r.get("account") or "",
|
|
122
|
+
"enabled": r.get("enabled") in ("1", True, 1, None),
|
|
123
|
+
"raw": r,
|
|
124
|
+
}
|
|
125
|
+
return _sorted_dict(result)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def list_certificates_simple():
|
|
129
|
+
return {k: v["status"] for k, v in list_certificates().items()}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def list_certificates_pretty():
|
|
133
|
+
data = list_certificates()
|
|
134
|
+
lines = []
|
|
135
|
+
for name in sorted(data):
|
|
136
|
+
info = data[name]
|
|
137
|
+
lines.append(f"{name} status={info.get('status')} uuid={info.get('uuid','')[:8]}")
|
|
138
|
+
return lines
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def list_settings():
|
|
142
|
+
try:
|
|
143
|
+
res = __salt__["opnsense.call"]("acmeclient", "settings", "get", method="GET")
|
|
144
|
+
return res
|
|
145
|
+
except Exception as exc:
|
|
146
|
+
log.debug("acme settings get failed: %s", exc)
|
|
147
|
+
return {}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def get_account(name):
|
|
151
|
+
accounts = list_accounts()
|
|
152
|
+
return accounts.get(name)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def get_validation(name):
|
|
156
|
+
vals = list_validations()
|
|
157
|
+
return vals.get(name)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def get_certificate(name):
|
|
161
|
+
certs = list_certificates()
|
|
162
|
+
return certs.get(name)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def service_status():
|
|
166
|
+
try:
|
|
167
|
+
res = __salt__["opnsense.call"]("acmeclient", "service", "status", method="POST", data={})
|
|
168
|
+
return res
|
|
169
|
+
except Exception as exc:
|
|
170
|
+
log.debug("acme service status failed: %s", exc)
|
|
171
|
+
return {"error": str(exc)}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
log = logging.getLogger(__name__)
|
|
4
|
+
|
|
5
|
+
__virtualname__ = "opnsense_bind"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def __virtual__():
|
|
9
|
+
if "opnsense.search" in __salt__ or "opnsense.call" in __salt__:
|
|
10
|
+
return __virtualname__
|
|
11
|
+
return (False, "opnsense execution module not loaded")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _search(controller, type_name, search_phrase="", row_count=-1):
|
|
15
|
+
try:
|
|
16
|
+
fn = __salt__["opnsense.search"]
|
|
17
|
+
res = fn("bind", controller, type_name, search_phrase=search_phrase, row_count=row_count)
|
|
18
|
+
if isinstance(res, dict):
|
|
19
|
+
return res.get("rows", [])
|
|
20
|
+
return []
|
|
21
|
+
except Exception as exc:
|
|
22
|
+
log.debug("bind search %s/%s failed: %s", controller, type_name, exc)
|
|
23
|
+
return []
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _search_domain_all():
|
|
27
|
+
all_rows = []
|
|
28
|
+
for c in ("domain",):
|
|
29
|
+
for t in ("primary_domain", "secondary_domain", "forward_domain", "master_domain", "slave_domain", "domain"):
|
|
30
|
+
try:
|
|
31
|
+
rows = _search(c, t, row_count=-1)
|
|
32
|
+
if rows:
|
|
33
|
+
for r in rows:
|
|
34
|
+
r["_bind_type"] = t
|
|
35
|
+
r["_bind_controller"] = c
|
|
36
|
+
all_rows.extend(rows)
|
|
37
|
+
except Exception:
|
|
38
|
+
continue
|
|
39
|
+
dedup = {}
|
|
40
|
+
for r in all_rows:
|
|
41
|
+
uuid = r.get("uuid")
|
|
42
|
+
if uuid and uuid not in dedup:
|
|
43
|
+
dedup[uuid] = r
|
|
44
|
+
elif not uuid:
|
|
45
|
+
key = r.get("domainname") or r.get("domain") or r.get("name")
|
|
46
|
+
if key and key not in dedup:
|
|
47
|
+
dedup[key] = r
|
|
48
|
+
return list(dedup.values())
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def list_domains(domain_type=None):
|
|
52
|
+
rows = _search_domain_all()
|
|
53
|
+
result = {}
|
|
54
|
+
for r in rows:
|
|
55
|
+
dname = r.get("domainname") or r.get("domain") or r.get("name") or ""
|
|
56
|
+
if not dname:
|
|
57
|
+
continue
|
|
58
|
+
btype = r.get("_bind_type", "primary_domain")
|
|
59
|
+
if domain_type and domain_type not in btype and domain_type != "all":
|
|
60
|
+
if domain_type == "primary" and "primary" not in btype:
|
|
61
|
+
continue
|
|
62
|
+
if domain_type == "secondary" and "secondary" not in btype:
|
|
63
|
+
continue
|
|
64
|
+
if domain_type == "forward" and "forward" not in btype:
|
|
65
|
+
continue
|
|
66
|
+
result[dname] = {
|
|
67
|
+
"domainname": dname,
|
|
68
|
+
"type": btype,
|
|
69
|
+
"uuid": r.get("uuid"),
|
|
70
|
+
"description": r.get("description", ""),
|
|
71
|
+
"enabled": r.get("enabled") in ("1", True, 1, None),
|
|
72
|
+
"raw": r,
|
|
73
|
+
}
|
|
74
|
+
return dict(sorted(result.items()))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def list_domains_simple():
|
|
78
|
+
return {k: v["uuid"] for k, v in list_domains().items()}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def list_domains_pretty(domain_type=None):
|
|
82
|
+
data = list_domains(domain_type=domain_type)
|
|
83
|
+
lines = []
|
|
84
|
+
for dname in sorted(data):
|
|
85
|
+
info = data[dname]
|
|
86
|
+
en = "enabled" if info.get("enabled") else "disabled"
|
|
87
|
+
lines.append(f"{dname} [{info.get('type')}] ({en}) {info.get('uuid','')[:8]}")
|
|
88
|
+
return lines
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _resolve_domain_uuid(domain):
|
|
92
|
+
if not domain:
|
|
93
|
+
return None
|
|
94
|
+
try:
|
|
95
|
+
from saltext.opnsense.utils.common import is_uuid
|
|
96
|
+
except Exception:
|
|
97
|
+
import re
|
|
98
|
+
|
|
99
|
+
def is_uuid(v):
|
|
100
|
+
return bool(re.match(r"^[0-9a-fA-F]{8}-", str(v)))
|
|
101
|
+
|
|
102
|
+
if is_uuid(domain):
|
|
103
|
+
return domain
|
|
104
|
+
domains = list_domains()
|
|
105
|
+
if domain in domains:
|
|
106
|
+
return domains[domain]["uuid"]
|
|
107
|
+
for rows in [_search_domain_all()]:
|
|
108
|
+
for r in rows:
|
|
109
|
+
if r.get("domainname") == domain:
|
|
110
|
+
return r.get("uuid")
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def list_records(domain=None, domain_filter=None, record_type=None, name=None):
|
|
115
|
+
if domain is None and domain_filter is not None:
|
|
116
|
+
domain = domain_filter
|
|
117
|
+
rows = _search("record", "record", row_count=-1)
|
|
118
|
+
domain_uuid_filter = None
|
|
119
|
+
domain_name_filter = None
|
|
120
|
+
if domain:
|
|
121
|
+
domain_uuid_filter = _resolve_domain_uuid(domain)
|
|
122
|
+
if domain_uuid_filter:
|
|
123
|
+
domains_map = {v["uuid"]: k for k, v in list_domains().items() if v.get("uuid")}
|
|
124
|
+
domain_name_filter = domains_map.get(domain_uuid_filter, domain)
|
|
125
|
+
if domain_name_filter is None or domain_name_filter == domain_uuid_filter:
|
|
126
|
+
domain_name_filter = domain if "." in str(domain) else None
|
|
127
|
+
else:
|
|
128
|
+
domain_uuid_filter = domain if domain and len(domain) > 20 else None
|
|
129
|
+
domain_name_filter = domain if "." in str(domain) else None
|
|
130
|
+
|
|
131
|
+
result = {}
|
|
132
|
+
for r in rows:
|
|
133
|
+
r_domain = r.get("domain") or r.get("domain_uuid") or ""
|
|
134
|
+
r_name = r.get("name") or ""
|
|
135
|
+
r_type = r.get("type") or ""
|
|
136
|
+
r_value = r.get("value") or r.get("server") or ""
|
|
137
|
+
|
|
138
|
+
if domain_uuid_filter:
|
|
139
|
+
if r_domain != domain_uuid_filter and r_domain != domain:
|
|
140
|
+
if domain_uuid_filter and r_domain != domain_uuid_filter:
|
|
141
|
+
continue
|
|
142
|
+
if record_type and r_type != record_type:
|
|
143
|
+
continue
|
|
144
|
+
if name and r_name != name:
|
|
145
|
+
continue
|
|
146
|
+
|
|
147
|
+
key = f"{r_name}.{domain_name_filter or r_domain}" if r_name else r.get("uuid", "")
|
|
148
|
+
if not key:
|
|
149
|
+
key = r.get("uuid", "")
|
|
150
|
+
|
|
151
|
+
result[key] = {
|
|
152
|
+
"name": r_name,
|
|
153
|
+
"domain": domain_name_filter or r_domain,
|
|
154
|
+
"domain_uuid": r_domain,
|
|
155
|
+
"type": r_type,
|
|
156
|
+
"value": r_value,
|
|
157
|
+
"ttl": r.get("ttl", ""),
|
|
158
|
+
"uuid": r.get("uuid"),
|
|
159
|
+
"description": r.get("description", ""),
|
|
160
|
+
"enabled": r.get("enabled") in ("1", True, 1, None),
|
|
161
|
+
"raw": r,
|
|
162
|
+
}
|
|
163
|
+
return dict(sorted(result.items()))
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def list_records_simple(domain=None, domain_filter=None):
|
|
167
|
+
if domain is None and domain_filter is not None:
|
|
168
|
+
domain = domain_filter
|
|
169
|
+
return list_records(domain=domain)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def list_records_pretty(domain=None, domain_filter=None, record_type=None):
|
|
173
|
+
if domain is None and domain_filter is not None:
|
|
174
|
+
domain = domain_filter
|
|
175
|
+
data = list_records(domain=domain, record_type=record_type)
|
|
176
|
+
lines = []
|
|
177
|
+
for key in sorted(data):
|
|
178
|
+
info = data[key]
|
|
179
|
+
lines.append(f"{info.get('name')} {info.get('type')} {info.get('value')}")
|
|
180
|
+
return lines
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def list_records_as_strings(domain=None, domain_filter=None):
|
|
184
|
+
return list_records_pretty(domain=domain, domain_filter=domain_filter)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def list_acls():
|
|
188
|
+
rows = _search("acl", "acl", row_count=-1)
|
|
189
|
+
result = {}
|
|
190
|
+
for r in rows:
|
|
191
|
+
name = r.get("name") or r.get("uuid") or ""
|
|
192
|
+
result[name] = {
|
|
193
|
+
"name": name,
|
|
194
|
+
"uuid": r.get("uuid"),
|
|
195
|
+
"description": r.get("description", ""),
|
|
196
|
+
"enabled": r.get("enabled") in ("1", True, 1),
|
|
197
|
+
"raw": r,
|
|
198
|
+
}
|
|
199
|
+
return dict(sorted(result.items()))
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def list_acls_pretty():
|
|
203
|
+
data = list_acls()
|
|
204
|
+
lines = []
|
|
205
|
+
for name in sorted(data):
|
|
206
|
+
info = data[name]
|
|
207
|
+
lines.append(f"{name} ({'enabled' if info.get('enabled') else 'disabled'}) {info.get('uuid','')[:8]}")
|
|
208
|
+
return lines
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def get_domain(name):
|
|
212
|
+
domains = list_domains()
|
|
213
|
+
return domains.get(name)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def get_record(name, domain=None):
|
|
217
|
+
records = list_records(domain=domain, name=name)
|
|
218
|
+
for k, v in records.items():
|
|
219
|
+
if v["name"] == name:
|
|
220
|
+
return v
|
|
221
|
+
return None
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def resolve_domain(name_or_uuid):
|
|
225
|
+
return _resolve_domain_uuid(name_or_uuid)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def get_domain_uuid(name_or_uuid):
|
|
229
|
+
return _resolve_domain_uuid(name_or_uuid)
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
log = logging.getLogger(__name__)
|
|
4
|
+
|
|
5
|
+
__virtualname__ = "opnsense_dns"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def __virtual__():
|
|
9
|
+
if "opnsense.search" in __salt__ or "opnsense_unbound.list_aliases" in __salt__:
|
|
10
|
+
return __virtualname__
|
|
11
|
+
if "opnsense.search" in __salt__:
|
|
12
|
+
return __virtualname__
|
|
13
|
+
return (False, "opnsense execution module not loaded")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _pillar_aliases():
|
|
17
|
+
try:
|
|
18
|
+
pillar = __pillar__ or {}
|
|
19
|
+
op = pillar.get("opnsense", {}) if isinstance(pillar, dict) else {}
|
|
20
|
+
return op.get("aliases", {}), op.get("purge_aliases", {}), op.get("cluster_parent", {})
|
|
21
|
+
except Exception:
|
|
22
|
+
return {}, {}, {}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def list_aliases(domain=None, parent=None):
|
|
26
|
+
"""
|
|
27
|
+
List DNS aliases as dict fqdn -> info.
|
|
28
|
+
|
|
29
|
+
Delightful alternative to raw search. Returns sorted dict keyed by FQDN.
|
|
30
|
+
|
|
31
|
+
Example:
|
|
32
|
+
salt opnsense-router opnsense_dns.list_aliases
|
|
33
|
+
salt opnsense-router opnsense_dns.list_aliases domain=example.com
|
|
34
|
+
salt opnsense-router opnsense_dns.list_aliases parent=cluster.example.com
|
|
35
|
+
"""
|
|
36
|
+
try:
|
|
37
|
+
fn = __salt__["opnsense_unbound.list_aliases"]
|
|
38
|
+
return fn(domain=domain, parent=parent)
|
|
39
|
+
except Exception:
|
|
40
|
+
try:
|
|
41
|
+
fn = __salt__["opnsense.search"]
|
|
42
|
+
res = fn("unbound", "settings", "host_alias", row_count=-1)
|
|
43
|
+
rows = res.get("rows", []) if isinstance(res, dict) else []
|
|
44
|
+
result = {}
|
|
45
|
+
for r in rows:
|
|
46
|
+
hn = r.get("hostname")
|
|
47
|
+
dom = r.get("domain")
|
|
48
|
+
if not hn or not dom:
|
|
49
|
+
continue
|
|
50
|
+
if domain and dom != domain:
|
|
51
|
+
continue
|
|
52
|
+
fqdn = f"{hn}.{dom}"
|
|
53
|
+
result[fqdn] = r.get("host")
|
|
54
|
+
return result
|
|
55
|
+
except Exception as exc:
|
|
56
|
+
log.debug("dns list_aliases failed: %s", exc)
|
|
57
|
+
return {}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def list_aliases_detailed(domain=None, parent=None):
|
|
61
|
+
"""
|
|
62
|
+
Alias to list_aliases returning full info dict.
|
|
63
|
+
|
|
64
|
+
Example:
|
|
65
|
+
salt opnsense-router opnsense_dns.list_aliases_detailed
|
|
66
|
+
"""
|
|
67
|
+
try:
|
|
68
|
+
fn = __salt__["opnsense_unbound.list_aliases"]
|
|
69
|
+
return fn(domain=domain, parent=parent)
|
|
70
|
+
except Exception:
|
|
71
|
+
return list_aliases(domain=domain, parent=parent)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def list_aliases_simple(domain=None, parent=None):
|
|
75
|
+
"""
|
|
76
|
+
Simple mapping fqdn -> parent FQDN. Great for CLI --out=table.
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
salt opnsense-router opnsense_dns.list_aliases_simple --out=table
|
|
80
|
+
salt opnsense-router opnsense_dns.list_aliases_simple domain=example.com
|
|
81
|
+
"""
|
|
82
|
+
try:
|
|
83
|
+
fn = __salt__["opnsense_unbound.list_aliases_simple"]
|
|
84
|
+
return fn(domain=domain, parent=parent)
|
|
85
|
+
except Exception:
|
|
86
|
+
try:
|
|
87
|
+
fn = __salt__["opnsense_unbound.list_aliases"]
|
|
88
|
+
data = fn(domain=domain, parent=parent)
|
|
89
|
+
return {fqdn: info.get("parent") if isinstance(info, dict) else info for fqdn, info in data.items()}
|
|
90
|
+
except Exception:
|
|
91
|
+
data = list_aliases(domain=domain, parent=parent)
|
|
92
|
+
return {fqdn: info.get("parent") if isinstance(info, dict) else str(info) for fqdn, info in data.items()}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def list_aliases_pretty(domain=None, parent=None):
|
|
96
|
+
"""
|
|
97
|
+
Pretty list of "fqdn -> parent (enabled)" for human output. Works with --out=table.
|
|
98
|
+
|
|
99
|
+
Example:
|
|
100
|
+
salt opnsense-router opnsense_dns.list_aliases_pretty --out=table
|
|
101
|
+
salt opnsense-router opnsense_dns.list_aliases_pretty domain=example.com --out=table
|
|
102
|
+
"""
|
|
103
|
+
try:
|
|
104
|
+
fn = __salt__["opnsense_unbound.list_aliases_pretty"]
|
|
105
|
+
return fn(domain=domain, parent=parent)
|
|
106
|
+
except Exception:
|
|
107
|
+
pass
|
|
108
|
+
try:
|
|
109
|
+
fn = __salt__["opnsense_unbound.list_aliases"]
|
|
110
|
+
data = fn(domain=domain, parent=parent)
|
|
111
|
+
except Exception:
|
|
112
|
+
data = list_aliases(domain=domain, parent=parent)
|
|
113
|
+
lines = []
|
|
114
|
+
for fqdn in sorted(data):
|
|
115
|
+
info = data[fqdn]
|
|
116
|
+
if isinstance(info, dict):
|
|
117
|
+
parent_f = info.get("parent", "")
|
|
118
|
+
en = "enabled" if info.get("enabled") else "disabled" if "enabled" in info else "enabled"
|
|
119
|
+
lines.append(f"{fqdn} -> {parent_f} ({en})")
|
|
120
|
+
else:
|
|
121
|
+
lines.append(f"{fqdn} -> {info}")
|
|
122
|
+
return lines
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def list_aliases_simple_pretty(domain=None, parent=None):
|
|
126
|
+
"""
|
|
127
|
+
Alias to list_aliases_pretty for backward compat.
|
|
128
|
+
"""
|
|
129
|
+
return list_aliases_pretty(domain=domain, parent=parent)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def list_pillar_aliases():
|
|
133
|
+
aliases, purge, parent = _pillar_aliases()
|
|
134
|
+
return {
|
|
135
|
+
"aliases": aliases,
|
|
136
|
+
"purge_aliases": purge,
|
|
137
|
+
"cluster_parent": parent,
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def resolve_parent(parent=None):
|
|
142
|
+
if parent:
|
|
143
|
+
try:
|
|
144
|
+
fn = __salt__["opnsense_unbound.resolve_parent"]
|
|
145
|
+
return fn(parent)
|
|
146
|
+
except Exception:
|
|
147
|
+
return None
|
|
148
|
+
_, _, cp = _pillar_aliases()
|
|
149
|
+
if isinstance(cp, dict) and cp.get("uuid"):
|
|
150
|
+
return cp["uuid"]
|
|
151
|
+
if isinstance(cp, dict) and cp.get("hostname"):
|
|
152
|
+
try:
|
|
153
|
+
fn = __salt__["opnsense_unbound.resolve_parent"]
|
|
154
|
+
fqdn = f"{cp['hostname']}.{cp.get('domain','example.com')}"
|
|
155
|
+
return fn(fqdn)
|
|
156
|
+
except Exception:
|
|
157
|
+
return None
|
|
158
|
+
return None
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def managed_preview(parent=None, aliases=None, purge=None):
|
|
162
|
+
aliases_p, purge_p, parent_p = _pillar_aliases()
|
|
163
|
+
if aliases is None:
|
|
164
|
+
aliases = aliases_p
|
|
165
|
+
if purge is None:
|
|
166
|
+
purge = purge_p
|
|
167
|
+
if parent is None:
|
|
168
|
+
if isinstance(parent_p, dict) and parent_p.get("hostname"):
|
|
169
|
+
parent = f"{parent_p['hostname']}.{parent_p.get('domain','example.com')}"
|
|
170
|
+
else:
|
|
171
|
+
parent = parent_p
|
|
172
|
+
live = list_aliases()
|
|
173
|
+
desired_fqdns = {f"{h}.{dom}" for dom, hosts in (aliases or {}).items() for h in (hosts or [])}
|
|
174
|
+
purge_fqdns = {f"{h}.{dom}" for dom, hosts in (purge or {}).items() for h in (hosts or [])}
|
|
175
|
+
return {
|
|
176
|
+
"parent": parent,
|
|
177
|
+
"desired": sorted(desired_fqdns),
|
|
178
|
+
"purge": sorted(purge_fqdns),
|
|
179
|
+
"live_count": len(live),
|
|
180
|
+
"live": live,
|
|
181
|
+
}
|