rpyc-pve-cloud 0.0.21__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.
- pve_cloud_rpc/_version.py +1 -0
- pve_cloud_rpc/protos/cloud_pb2.py +75 -0
- pve_cloud_rpc/protos/cloud_pb2_grpc.py +398 -0
- pve_cloud_rpc/protos/health_pb2.py +43 -0
- pve_cloud_rpc/protos/health_pb2_grpc.py +97 -0
- pve_cloud_rpc/server.py +169 -0
- rpyc_pve_cloud-0.0.21.dist-info/METADATA +10 -0
- rpyc_pve_cloud-0.0.21.dist-info/RECORD +12 -0
- rpyc_pve_cloud-0.0.21.dist-info/WHEEL +5 -0
- rpyc_pve_cloud-0.0.21.dist-info/entry_points.txt +2 -0
- rpyc_pve_cloud-0.0.21.dist-info/licenses/LICENSE.md +660 -0
- rpyc_pve_cloud-0.0.21.dist-info/top_level.txt +1 -0
pve_cloud_rpc/server.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import asyncio, asyncssh
|
|
2
|
+
import grpc
|
|
3
|
+
import pve_cloud_rpc.protos.cloud_pb2 as cloud_pb2
|
|
4
|
+
import pve_cloud_rpc.protos.cloud_pb2_grpc as cloud_pb2_grpc
|
|
5
|
+
import pve_cloud_rpc.protos.health_pb2 as health_pb2
|
|
6
|
+
import pve_cloud_rpc.protos.health_pb2_grpc as health_pb2_grpc
|
|
7
|
+
from pve_cloud.lib.inventory import *
|
|
8
|
+
from pve_cloud.cli.pvclu import get_cluster_vars, get_ssh_master_kubeconfig
|
|
9
|
+
import yaml
|
|
10
|
+
import socket
|
|
11
|
+
import sys
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class HealthServicer(health_pb2_grpc.HealthServicer):
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self._statuses = {}
|
|
17
|
+
|
|
18
|
+
async def Check(self, request, context):
|
|
19
|
+
service_name = request.service or ""
|
|
20
|
+
status = self._statuses.get(service_name, health_pb2.HealthCheckResponse.UNKNOWN)
|
|
21
|
+
print(f"Health Check request for service '{service_name}', returning {status}")
|
|
22
|
+
return health_pb2.HealthCheckResponse(status=status)
|
|
23
|
+
|
|
24
|
+
def set_status(self, service_name: str, status: health_pb2.HealthCheckResponse.ServingStatus):
|
|
25
|
+
self._statuses[service_name] = status
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class CloudServiceServicer(cloud_pb2_grpc.CloudServiceServicer):
|
|
29
|
+
|
|
30
|
+
async def GetMasterKubeconfig(self, request, context):
|
|
31
|
+
target_pve = request.target_pve
|
|
32
|
+
stack_name = request.stack_name
|
|
33
|
+
|
|
34
|
+
online_pve_host = get_online_pve_host(target_pve, skip_py_cloud_check=True)
|
|
35
|
+
cluster_vars = get_cluster_vars(online_pve_host)
|
|
36
|
+
|
|
37
|
+
return cloud_pb2.GetKubeconfigResponse(config=get_ssh_master_kubeconfig(cluster_vars, stack_name))
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
async def GetClusterVars(self, request, context):
|
|
41
|
+
target_pve = request.target_pve
|
|
42
|
+
|
|
43
|
+
online_pve_host = get_online_pve_host(target_pve, skip_py_cloud_check=True)
|
|
44
|
+
cluster_vars = get_cluster_vars(online_pve_host)
|
|
45
|
+
|
|
46
|
+
return cloud_pb2.GetClusterVarsResponse(vars=yaml.safe_dump(cluster_vars))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
async def GetCloudSecret(self, request, context):
|
|
50
|
+
target_pve = request.target_pve
|
|
51
|
+
secret_name = request.secret_name
|
|
52
|
+
|
|
53
|
+
online_pve_host = get_online_pve_host(target_pve, skip_py_cloud_check=True)
|
|
54
|
+
async with asyncssh.connect(online_pve_host, username='root', known_hosts=None) as conn:
|
|
55
|
+
cmd = await conn.run(f"cat /etc/pve/cloud/secrets/{secret_name}", check=True)
|
|
56
|
+
catted_secret = cmd.stdout
|
|
57
|
+
|
|
58
|
+
return cloud_pb2.GetCloudSecretResponse(secret=catted_secret)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
async def GetCephAccess(self, request, context):
|
|
62
|
+
target_pve = request.target_pve
|
|
63
|
+
|
|
64
|
+
online_pve_host = get_online_pve_host(target_pve, skip_py_cloud_check=True)
|
|
65
|
+
async with asyncssh.connect(online_pve_host, username='root', known_hosts=None) as conn:
|
|
66
|
+
cmd = await conn.run(f"cat /etc/ceph/ceph.conf", check=True)
|
|
67
|
+
catted_conf = cmd.stdout
|
|
68
|
+
|
|
69
|
+
cmd = await conn.run(f"cat /etc/pve/priv/ceph.client.admin.keyring", check=True)
|
|
70
|
+
catted_keyring = cmd.stdout
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
return cloud_pb2.GetCephAccessResponse(ceph_conf=catted_conf, admin_keyring=catted_keyring)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
async def GetSshKey(self, request, context):
|
|
77
|
+
target_pve = request.target_pve
|
|
78
|
+
|
|
79
|
+
online_pve_host = get_online_pve_host(target_pve, skip_py_cloud_check=True)
|
|
80
|
+
async with asyncssh.connect(online_pve_host, username='root', known_hosts=None) as conn:
|
|
81
|
+
match request.key_type:
|
|
82
|
+
case cloud_pb2.GetSshKeyRequest.PVE_HOST_RSA:
|
|
83
|
+
cmd = await conn.run(f"cat /root/.ssh/id_rsa", check=True)
|
|
84
|
+
catted_key = cmd.stdout
|
|
85
|
+
case cloud_pb2.GetSshKeyRequest.AUTOMATION:
|
|
86
|
+
cmd = await conn.run(f"cat /etc/pve/cloud/automation_id_ed25519", check=True)
|
|
87
|
+
catted_key = cmd.stdout
|
|
88
|
+
|
|
89
|
+
return cloud_pb2.GetSshKeyResponse(key=catted_key)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
async def GetProxmoxApi(self, request, context):
|
|
93
|
+
target_pve = request.target_pve
|
|
94
|
+
|
|
95
|
+
online_pve_host = get_online_pve_host(target_pve, skip_py_cloud_check=True)
|
|
96
|
+
async with asyncssh.connect(online_pve_host, username='root', known_hosts=None) as conn:
|
|
97
|
+
args_string = None
|
|
98
|
+
if request.get_args:
|
|
99
|
+
args_string = " ".join(f"{k} {v}" for k, v in request.get_args.items())
|
|
100
|
+
|
|
101
|
+
cmd = await conn.run(f"pvesh get {request.api_path} {args_string} --output-format json", check=True)
|
|
102
|
+
resp_json = cmd.stdout
|
|
103
|
+
|
|
104
|
+
return cloud_pb2.GetProxmoxApiResponse(json_resp=resp_json)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
async def GetProxmoxHost(self, request, context):
|
|
108
|
+
target_pve = request.target_pve
|
|
109
|
+
online_pve_host = get_online_pve_host(target_pve, skip_py_cloud_check=True)
|
|
110
|
+
|
|
111
|
+
return cloud_pb2.GetProxmoxHostResponse(pve_host=online_pve_host)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
async def GetPveInventory(self, request, context):
|
|
115
|
+
target_pve = request.target_pve
|
|
116
|
+
|
|
117
|
+
cloud_domain = get_cloud_domain(target_pve)
|
|
118
|
+
pve_inventory = get_pve_inventory(cloud_domain, skip_py_cloud_check=True)
|
|
119
|
+
|
|
120
|
+
return cloud_pb2.GetPveInventoryResponse(inventory=yaml.safe_dump(pve_inventory), cloud_domain=cloud_domain)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def is_port_bound(port, host="0.0.0.0"):
|
|
124
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
125
|
+
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
126
|
+
try:
|
|
127
|
+
s.bind((host, port))
|
|
128
|
+
return False # not bound
|
|
129
|
+
except OSError:
|
|
130
|
+
return True # bound
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
async def serve():
|
|
134
|
+
server = grpc.aio.server()
|
|
135
|
+
cloud_pb2_grpc.add_CloudServiceServicer_to_server(CloudServiceServicer(), server)
|
|
136
|
+
|
|
137
|
+
health_servicer = HealthServicer()
|
|
138
|
+
health_pb2_grpc.add_HealthServicer_to_server(health_servicer, server)
|
|
139
|
+
|
|
140
|
+
# if is_port_bound(50052): # google quirks nice
|
|
141
|
+
# raise RuntimeError("PCRPC Already running / unclean shutdown!")
|
|
142
|
+
|
|
143
|
+
socket_file = f"/tmp/pc-rpc-{sys.argv[1]}.sock"
|
|
144
|
+
|
|
145
|
+
# listen_addr = "[::]:50052"
|
|
146
|
+
server.add_insecure_port(f"unix://{socket_file}")
|
|
147
|
+
await server.start()
|
|
148
|
+
|
|
149
|
+
health_servicer.set_status(
|
|
150
|
+
"", # empty = overall server health
|
|
151
|
+
health_pb2.HealthCheckResponse.SERVING
|
|
152
|
+
)
|
|
153
|
+
# print(f"gRPC AsyncIO server running on {listen_addr}")
|
|
154
|
+
print(f"gRPC AsyncIO server running on {socket_file}")
|
|
155
|
+
try:
|
|
156
|
+
await server.wait_for_termination()
|
|
157
|
+
finally:
|
|
158
|
+
# Ensure cleanup
|
|
159
|
+
await server.stop(grace=0)
|
|
160
|
+
print("gRPC server stopped and port released.")
|
|
161
|
+
|
|
162
|
+
# delete unix socket file
|
|
163
|
+
if os.path.exists(socket_file):
|
|
164
|
+
os.remove(socket_file)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def main():
|
|
169
|
+
asyncio.run(serve())
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rpyc-pve-cloud
|
|
3
|
+
Version: 0.0.21
|
|
4
|
+
Author-email: Tobias Huebner <tobias.huebner@vmzberlin.com>
|
|
5
|
+
License-Expression: GPL-3.0-or-later
|
|
6
|
+
License-File: LICENSE.md
|
|
7
|
+
Requires-Dist: py-pve-cloud<0.14.0,>=0.13.8
|
|
8
|
+
Requires-Dist: grpcio==1.76.0
|
|
9
|
+
Requires-Dist: asyncssh==2.21.0
|
|
10
|
+
Dynamic: license-file
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
pve_cloud_rpc/_version.py,sha256=PsqtE_T084MVsMv47JyTQ3DK2CRZJ3Kd9Q_vnw02oZk,23
|
|
2
|
+
pve_cloud_rpc/server.py,sha256=gupcL5VHLTJUfZPgBn04H6tpNdUykvSylbBXd4YPg2U,6439
|
|
3
|
+
pve_cloud_rpc/protos/cloud_pb2.py,sha256=WZyD6MaxEnEXHXDzLrkk1O0WvoRTmuS9wY2UXZm29JE,6153
|
|
4
|
+
pve_cloud_rpc/protos/cloud_pb2_grpc.py,sha256=FOC_MXEZkXe32dzxQua9zHBLWLjso1X-xqj0xTexfFg,15535
|
|
5
|
+
pve_cloud_rpc/protos/health_pb2.py,sha256=GuDLSR8jFZuEk4cNL0UMEkTerscanrgOFroe-gee-Nc,2238
|
|
6
|
+
pve_cloud_rpc/protos/health_pb2_grpc.py,sha256=XXT8lsH8YgKLqn07YfigSQ6TNW6s-P4V6S-X69fLfh0,3368
|
|
7
|
+
rpyc_pve_cloud-0.0.21.dist-info/licenses/LICENSE.md,sha256=ADUqsZhl4juwq34PRTMiBqumpm11s_PMli_dZQjWPqQ,34260
|
|
8
|
+
rpyc_pve_cloud-0.0.21.dist-info/METADATA,sha256=c016rbTMiSinbW4O7wa8P22pOWVuFy_z8o7mV4P4syw,309
|
|
9
|
+
rpyc_pve_cloud-0.0.21.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
10
|
+
rpyc_pve_cloud-0.0.21.dist-info/entry_points.txt,sha256=h4ytTryc_8IlySP_ICyy6srHzF71qrQ0noUhqI-3aGw,52
|
|
11
|
+
rpyc_pve_cloud-0.0.21.dist-info/top_level.txt,sha256=CTGM4RRp33khtroUiv-RiYXI4h6W6AaQQGIvsbwAetM,14
|
|
12
|
+
rpyc_pve_cloud-0.0.21.dist-info/RECORD,,
|