syneto_api 0.3.76__tar.gz → 0.3.78__tar.gz
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.
- {syneto_api-0.3.76 → syneto_api-0.3.78}/PKG-INFO +1 -1
- {syneto_api-0.3.76 → syneto_api-0.3.78}/pyproject.toml +1 -1
- syneto_api-0.3.78/src/syneto_api/apollo_api.py +181 -0
- {syneto_api-0.3.76 → syneto_api-0.3.78}/src/syneto_api/storage_api.py +3 -2
- syneto_api-0.3.76/src/syneto_api/apollo_api.py +0 -94
- {syneto_api-0.3.76 → syneto_api-0.3.78}/README.md +0 -0
- {syneto_api-0.3.76 → syneto_api-0.3.78}/src/syneto_api/__init__.py +0 -0
- {syneto_api-0.3.76 → syneto_api-0.3.78}/src/syneto_api/api_client.py +0 -0
- {syneto_api-0.3.76 → syneto_api-0.3.78}/src/syneto_api/auth_api.py +0 -0
- {syneto_api-0.3.76 → syneto_api-0.3.78}/src/syneto_api/central_api.py +0 -0
- {syneto_api-0.3.76 → syneto_api-0.3.78}/src/syneto_api/jobs_api.py +0 -0
- {syneto_api-0.3.76 → syneto_api-0.3.78}/src/syneto_api/keyring.py +0 -0
- {syneto_api-0.3.76 → syneto_api-0.3.78}/src/syneto_api/node_api.py +0 -0
- {syneto_api-0.3.76 → syneto_api-0.3.78}/src/syneto_api/protection_api.py +0 -0
- {syneto_api-0.3.76 → syneto_api-0.3.78}/src/syneto_api/settings.py +0 -0
- {syneto_api-0.3.76 → syneto_api-0.3.78}/src/syneto_api/virt_api.py +0 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from .api_client import APIClientBase, InvalidInputException
|
|
4
|
+
|
|
5
|
+
# Sentinel used by update_identity to distinguish "field not provided"
|
|
6
|
+
# (omit from the JSON body) from "explicit null" (include as JSON null).
|
|
7
|
+
_UNSET = object()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Apollo(APIClientBase):
|
|
11
|
+
"""Client for the Apollo networking service.
|
|
12
|
+
|
|
13
|
+
Apollo is a host-local service providing network management (vSwitches, port groups, NICs,
|
|
14
|
+
hostname, service address resolution).
|
|
15
|
+
|
|
16
|
+
By default, connects directly to Apollo at the APOLLO_SERVICE env var (e.g. http://172.16.254.2:6000).
|
|
17
|
+
|
|
18
|
+
TODO: Add Minerva proxy support. When Minerva proxy is available, Apollo endpoints will be
|
|
19
|
+
accessed via /api/virtualization/v1/computeHosts/{id}/network/v1/... through Minerva.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, url_base=None, custom_retry_config=None, **kwargs):
|
|
23
|
+
super().__init__(url_base or os.environ.get("APOLLO_SERVICE", ""), **kwargs)
|
|
24
|
+
self._custom_retry_config = custom_retry_config
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def _retry_config(self):
|
|
28
|
+
return self._custom_retry_config or super()._retry_config
|
|
29
|
+
|
|
30
|
+
# --- vSwitches ---
|
|
31
|
+
|
|
32
|
+
def get_vswitches(self):
|
|
33
|
+
"""List all vSwitches."""
|
|
34
|
+
return self.get_request("/v1/vswitches")
|
|
35
|
+
|
|
36
|
+
def get_vswitch(self, vswitch_id: str):
|
|
37
|
+
"""Get a single vSwitch by ID."""
|
|
38
|
+
if not vswitch_id:
|
|
39
|
+
raise InvalidInputException(f"Expected a valid vswitch id, received: `{vswitch_id}`")
|
|
40
|
+
return self.get_request("/v1/vswitches/{id}", id=vswitch_id)
|
|
41
|
+
|
|
42
|
+
# --- Port Groups ---
|
|
43
|
+
|
|
44
|
+
def get_port_groups(self, tag: str = None):
|
|
45
|
+
"""List all port groups, with optional tag filter."""
|
|
46
|
+
query_args = {}
|
|
47
|
+
if tag:
|
|
48
|
+
query_args["tag"] = tag
|
|
49
|
+
return self.get_request("/v1/port-groups", query_args=query_args or None)
|
|
50
|
+
|
|
51
|
+
def get_port_group(self, port_group_id: str):
|
|
52
|
+
"""Get a single port group by ID."""
|
|
53
|
+
if not port_group_id:
|
|
54
|
+
raise InvalidInputException(f"Expected a valid port group id, received: `{port_group_id}`")
|
|
55
|
+
return self.get_request("/v1/port-groups/{id}", id=port_group_id)
|
|
56
|
+
|
|
57
|
+
def patch_port_group(self, vswitch_id: str, port_group_id: str, body: dict):
|
|
58
|
+
"""PATCH a port group's configuration.
|
|
59
|
+
|
|
60
|
+
The body is passed through to Apollo unchanged. For a vmkernel PG's
|
|
61
|
+
``networkConfig``, the request shape is flat:
|
|
62
|
+
|
|
63
|
+
.. code-block:: python
|
|
64
|
+
|
|
65
|
+
{
|
|
66
|
+
"networkConfig": {
|
|
67
|
+
"method": "dhcp", # "dhcp" / "static" / "none"
|
|
68
|
+
"dnsServers": ["8.8.8.8"], # configured override
|
|
69
|
+
"searchDomains": ["override.lan"], # configured override
|
|
70
|
+
"ignoreAutoDns": True, # only on dhcp; replace lease DNS
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
Response shape carries DHCP-pushed values for read-only inspection:
|
|
75
|
+
|
|
76
|
+
.. code-block:: python
|
|
77
|
+
|
|
78
|
+
"networkConfig": {
|
|
79
|
+
"dnsServers": {"configured": [...], "fromLease": [...]},
|
|
80
|
+
"searchDomains": {"configured": [...], "fromLease": [...]},
|
|
81
|
+
"ignoreAutoDns": True | False | None,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
Other fields the request accepts: ``name``, ``tags``, ``vlanConfig``.
|
|
85
|
+
Apollo enforces strict validation (extra keys rejected with 422).
|
|
86
|
+
"""
|
|
87
|
+
if not vswitch_id:
|
|
88
|
+
raise InvalidInputException(f"Expected a valid vswitch id, received: `{vswitch_id}`")
|
|
89
|
+
if not port_group_id:
|
|
90
|
+
raise InvalidInputException(f"Expected a valid port group id, received: `{port_group_id}`")
|
|
91
|
+
if not isinstance(body, dict) or not body:
|
|
92
|
+
raise InvalidInputException("Expected a non-empty body dict for port-group PATCH")
|
|
93
|
+
return self.patch_request(
|
|
94
|
+
"/v1/vswitches/{id}/port-groups/{pg_id}",
|
|
95
|
+
body=body,
|
|
96
|
+
id=vswitch_id,
|
|
97
|
+
pg_id=port_group_id,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# --- NICs ---
|
|
101
|
+
|
|
102
|
+
def get_nics(self):
|
|
103
|
+
"""List all NICs."""
|
|
104
|
+
return self.get_request("/v1/nics")
|
|
105
|
+
|
|
106
|
+
def get_nic(self, name: str):
|
|
107
|
+
"""Get a single NIC by name."""
|
|
108
|
+
if not name:
|
|
109
|
+
raise InvalidInputException(f"Expected a valid NIC name, received: `{name}`")
|
|
110
|
+
return self.get_request("/v1/nics/{name}", name=name)
|
|
111
|
+
|
|
112
|
+
def enable_nic(self, name: str):
|
|
113
|
+
"""Enable a NIC."""
|
|
114
|
+
if not name:
|
|
115
|
+
raise InvalidInputException(f"Expected a valid NIC name, received: `{name}`")
|
|
116
|
+
return self.post_request("/v1/nics/{name}:enable", name=name)
|
|
117
|
+
|
|
118
|
+
def disable_nic(self, name: str):
|
|
119
|
+
"""Disable a NIC."""
|
|
120
|
+
if not name:
|
|
121
|
+
raise InvalidInputException(f"Expected a valid NIC name, received: `{name}`")
|
|
122
|
+
return self.post_request("/v1/nics/{name}:disable", name=name)
|
|
123
|
+
|
|
124
|
+
# --- System ---
|
|
125
|
+
|
|
126
|
+
def get_service_addresses(self, tag: str):
|
|
127
|
+
"""Resolve service addresses for a given tag."""
|
|
128
|
+
if not tag:
|
|
129
|
+
raise InvalidInputException(f"Expected a valid tag, received: `{tag}`")
|
|
130
|
+
return self.get_request("/v1/system/service-addresses/{tag}", tag=tag)
|
|
131
|
+
|
|
132
|
+
def get_identity(self):
|
|
133
|
+
"""Get the system identity (short hostname + primary domain + derived FQDN).
|
|
134
|
+
|
|
135
|
+
Response keys:
|
|
136
|
+
- ``shortHostname``: the short RFC 1123 label
|
|
137
|
+
- ``primaryDomain``: the configured primary domain, or ``None`` when the
|
|
138
|
+
FQDN collapses to the short hostname
|
|
139
|
+
- ``fqdn``: the derived fully-qualified name (read from ``hostname -f``)
|
|
140
|
+
- ``orphaned``: ``True`` when the configured primary no longer appears
|
|
141
|
+
in the management-PG candidate union
|
|
142
|
+
- ``primaryDomainCandidates``: deduplicated, lexicographically ordered
|
|
143
|
+
list of ``{"domain": str, "origin": list[str]}`` entries from
|
|
144
|
+
management-tagged PG ``ipv4.dns-search`` values (and DHCP-supplied
|
|
145
|
+
search domains for DHCP-method connections)
|
|
146
|
+
"""
|
|
147
|
+
return self.get_request("/v1/system/identity")
|
|
148
|
+
|
|
149
|
+
def update_identity(self, short_hostname=_UNSET, primary_domain=_UNSET):
|
|
150
|
+
"""Update the system identity.
|
|
151
|
+
|
|
152
|
+
Each field is independently optional. Two distinct signals exist for
|
|
153
|
+
``primary_domain``:
|
|
154
|
+
|
|
155
|
+
- omitted (default sentinel) -- "do not touch the primary domain";
|
|
156
|
+
the field is left out of the JSON body entirely
|
|
157
|
+
- ``None`` -- "clear the primary domain"; the field is sent as JSON
|
|
158
|
+
``null`` so the FQDN collapses to the short hostname
|
|
159
|
+
- a string -- set the primary domain to that value (must be one of
|
|
160
|
+
the candidates returned by :py:meth:`get_identity`)
|
|
161
|
+
|
|
162
|
+
``short_hostname`` follows the same shape: omit to leave unchanged, or
|
|
163
|
+
pass a single RFC 1123 label (no dots) to update it. At least one of
|
|
164
|
+
the two fields must be provided; passing neither sends an empty body
|
|
165
|
+
and is rejected by the server.
|
|
166
|
+
|
|
167
|
+
Response keys:
|
|
168
|
+
- ``shortHostname``, ``primaryDomain``, ``fqdn``: the new state
|
|
169
|
+
- ``rebootRecommended``: ``True`` after any identity change
|
|
170
|
+
- ``sideEffects``: ``{"hostsFileUpdated": bool, "certificateRenewed": bool}``
|
|
171
|
+
"""
|
|
172
|
+
body: dict = {}
|
|
173
|
+
if short_hostname is not _UNSET:
|
|
174
|
+
if not short_hostname:
|
|
175
|
+
raise InvalidInputException(f"Expected a valid short hostname, received: `{short_hostname}`")
|
|
176
|
+
body["shortHostname"] = short_hostname
|
|
177
|
+
if primary_domain is not _UNSET:
|
|
178
|
+
body["primaryDomain"] = primary_domain
|
|
179
|
+
if not body:
|
|
180
|
+
raise InvalidInputException("Expected at least one of short_hostname or primary_domain to be provided")
|
|
181
|
+
return self.patch_request("/v1/system/identity", body=body)
|
|
@@ -9,8 +9,9 @@ class Storage(APIClientBase):
|
|
|
9
9
|
def __init__(self, url_base=None, **kwargs):
|
|
10
10
|
super().__init__(url_base or os.environ.get("STORAGE_SERVICE", ""), **kwargs)
|
|
11
11
|
|
|
12
|
-
def get_pools(self):
|
|
13
|
-
|
|
12
|
+
def get_pools(self, fields: list = None):
|
|
13
|
+
fields = {"fields": ",".join(fields)} if fields else None
|
|
14
|
+
return self.get_request("/pools", query_args=fields)
|
|
14
15
|
|
|
15
16
|
def get_importable_pools(self):
|
|
16
17
|
return self.get_request("/pools/importable")
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
from .api_client import APIClientBase, InvalidInputException
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class Apollo(APIClientBase):
|
|
7
|
-
"""Client for the Apollo networking service.
|
|
8
|
-
|
|
9
|
-
Apollo is a host-local service providing network management (vSwitches, port groups, NICs,
|
|
10
|
-
hostname, service address resolution).
|
|
11
|
-
|
|
12
|
-
By default, connects directly to Apollo at the APOLLO_SERVICE env var (e.g. http://172.16.254.2:6000).
|
|
13
|
-
|
|
14
|
-
TODO: Add Minerva proxy support. When Minerva proxy is available, Apollo endpoints will be
|
|
15
|
-
accessed via /api/virtualization/v1/computeHosts/{id}/network/v1/... through Minerva.
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
def __init__(self, url_base=None, custom_retry_config=None, **kwargs):
|
|
19
|
-
super().__init__(url_base or os.environ.get("APOLLO_SERVICE", ""), **kwargs)
|
|
20
|
-
self._custom_retry_config = custom_retry_config
|
|
21
|
-
|
|
22
|
-
@property
|
|
23
|
-
def _retry_config(self):
|
|
24
|
-
return self._custom_retry_config or super()._retry_config
|
|
25
|
-
|
|
26
|
-
# --- vSwitches ---
|
|
27
|
-
|
|
28
|
-
def get_vswitches(self):
|
|
29
|
-
"""List all vSwitches."""
|
|
30
|
-
return self.get_request("/v1/vswitches")
|
|
31
|
-
|
|
32
|
-
def get_vswitch(self, vswitch_id: str):
|
|
33
|
-
"""Get a single vSwitch by ID."""
|
|
34
|
-
if not vswitch_id:
|
|
35
|
-
raise InvalidInputException(f"Expected a valid vswitch id, received: `{vswitch_id}`")
|
|
36
|
-
return self.get_request("/v1/vswitches/{id}", id=vswitch_id)
|
|
37
|
-
|
|
38
|
-
# --- Port Groups ---
|
|
39
|
-
|
|
40
|
-
def get_port_groups(self, tag: str = None):
|
|
41
|
-
"""List all port groups, with optional tag filter."""
|
|
42
|
-
query_args = {}
|
|
43
|
-
if tag:
|
|
44
|
-
query_args["tag"] = tag
|
|
45
|
-
return self.get_request("/v1/port-groups", query_args=query_args or None)
|
|
46
|
-
|
|
47
|
-
def get_port_group(self, port_group_id: str):
|
|
48
|
-
"""Get a single port group by ID."""
|
|
49
|
-
if not port_group_id:
|
|
50
|
-
raise InvalidInputException(f"Expected a valid port group id, received: `{port_group_id}`")
|
|
51
|
-
return self.get_request("/v1/port-groups/{id}", id=port_group_id)
|
|
52
|
-
|
|
53
|
-
# --- NICs ---
|
|
54
|
-
|
|
55
|
-
def get_nics(self):
|
|
56
|
-
"""List all NICs."""
|
|
57
|
-
return self.get_request("/v1/nics")
|
|
58
|
-
|
|
59
|
-
def get_nic(self, name: str):
|
|
60
|
-
"""Get a single NIC by name."""
|
|
61
|
-
if not name:
|
|
62
|
-
raise InvalidInputException(f"Expected a valid NIC name, received: `{name}`")
|
|
63
|
-
return self.get_request("/v1/nics/{name}", name=name)
|
|
64
|
-
|
|
65
|
-
def enable_nic(self, name: str):
|
|
66
|
-
"""Enable a NIC."""
|
|
67
|
-
if not name:
|
|
68
|
-
raise InvalidInputException(f"Expected a valid NIC name, received: `{name}`")
|
|
69
|
-
return self.post_request("/v1/nics/{name}:enable", name=name)
|
|
70
|
-
|
|
71
|
-
def disable_nic(self, name: str):
|
|
72
|
-
"""Disable a NIC."""
|
|
73
|
-
if not name:
|
|
74
|
-
raise InvalidInputException(f"Expected a valid NIC name, received: `{name}`")
|
|
75
|
-
return self.post_request("/v1/nics/{name}:disable", name=name)
|
|
76
|
-
|
|
77
|
-
# --- System ---
|
|
78
|
-
|
|
79
|
-
def get_service_addresses(self, tag: str):
|
|
80
|
-
"""Resolve service addresses for a given tag."""
|
|
81
|
-
if not tag:
|
|
82
|
-
raise InvalidInputException(f"Expected a valid tag, received: `{tag}`")
|
|
83
|
-
return self.get_request("/v1/system/service-addresses/{tag}", tag=tag)
|
|
84
|
-
|
|
85
|
-
def get_hostname(self):
|
|
86
|
-
"""Get the system hostname."""
|
|
87
|
-
return self.get_request("/v1/system/hostname")
|
|
88
|
-
|
|
89
|
-
def update_hostname(self, hostname: str):
|
|
90
|
-
"""Update the system hostname."""
|
|
91
|
-
if not hostname:
|
|
92
|
-
raise InvalidInputException(f"Expected a valid hostname, received: `{hostname}`")
|
|
93
|
-
body = {"hostname": hostname}
|
|
94
|
-
return self.patch_request("/v1/system/hostname", body=body)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|