request-vm-on-golem 0.1.45__tar.gz → 0.1.47__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.
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/PKG-INFO +17 -3
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/README.md +16 -2
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/pyproject.toml +6 -1
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/cli/commands.py +133 -6
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/config.py +50 -4
- request_vm_on_golem-0.1.47/requestor/data/deployments/l2.json +9 -0
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/provider/client.py +10 -0
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/__init__.py +0 -0
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/api/main.py +0 -0
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/cli/__init__.py +0 -0
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/db/__init__.py +0 -0
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/db/sqlite.py +0 -0
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/errors.py +0 -0
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/payments/blockchain_service.py +0 -0
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/provider/__init__.py +0 -0
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/run.py +0 -0
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/security/faucet.py +0 -0
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/services/__init__.py +0 -0
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/services/database_service.py +0 -0
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/services/provider_service.py +0 -0
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/services/ssh_service.py +0 -0
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/services/vm_service.py +0 -0
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/ssh/__init__.py +0 -0
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/ssh/manager.py +0 -0
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/utils/logging.py +0 -0
- {request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/utils/spinner.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: request-vm-on-golem
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.47
|
4
4
|
Summary: VM on Golem Requestor CLI - Create and manage virtual machines on the Golem Network
|
5
5
|
Keywords: golem,vm,cloud,decentralized,cli
|
6
6
|
Author: Phillip Jensen
|
@@ -158,6 +158,19 @@ poetry run golem vm stream topup --stream-id 123 --hours 3
|
|
158
158
|
poetry run golem vm stream topup --stream-id 123 --glm 25.0
|
159
159
|
```
|
160
160
|
|
161
|
+
- Check stream status via provider (by VM name recorded in your DB):
|
162
|
+
|
163
|
+
```bash
|
164
|
+
poetry run golem vm stream status my-vm
|
165
|
+
# add --json for machine-readable output
|
166
|
+
```
|
167
|
+
|
168
|
+
- Inspect a stream directly on-chain:
|
169
|
+
|
170
|
+
```bash
|
171
|
+
poetry run golem vm stream inspect --stream-id 123
|
172
|
+
```
|
173
|
+
|
161
174
|
- Create a VM and attach an existing stream (no auto-streams are created by the requestor):
|
162
175
|
|
163
176
|
```bash
|
@@ -170,8 +183,9 @@ poetry run golem vm create my-vm \
|
|
170
183
|
Environment (env prefix `GOLEM_REQUESTOR_`):
|
171
184
|
|
172
185
|
- `polygon_rpc_url` — EVM RPC URL (default L2 RPC)
|
173
|
-
- `stream_payment_address` — StreamPayment address (
|
174
|
-
- `glm_token_address` — Token address
|
186
|
+
- `stream_payment_address` — StreamPayment address (defaults from `contracts/deployments/l2.json`; overridden by provider info)
|
187
|
+
- `glm_token_address` — Token address (defaults from `contracts/deployments/l2.json`; zero address means native ETH)
|
188
|
+
- Optional override of deployments directory: set `GOLEM_DEPLOYMENTS_DIR` to a folder containing `l2.json`.
|
175
189
|
- `provider_eth_address` — optional dev helper; in production always use `/provider/info`
|
176
190
|
- `network` — Target network for discovery filtering: `testnet` (default) or `mainnet`
|
177
191
|
|
@@ -117,6 +117,19 @@ poetry run golem vm stream topup --stream-id 123 --hours 3
|
|
117
117
|
poetry run golem vm stream topup --stream-id 123 --glm 25.0
|
118
118
|
```
|
119
119
|
|
120
|
+
- Check stream status via provider (by VM name recorded in your DB):
|
121
|
+
|
122
|
+
```bash
|
123
|
+
poetry run golem vm stream status my-vm
|
124
|
+
# add --json for machine-readable output
|
125
|
+
```
|
126
|
+
|
127
|
+
- Inspect a stream directly on-chain:
|
128
|
+
|
129
|
+
```bash
|
130
|
+
poetry run golem vm stream inspect --stream-id 123
|
131
|
+
```
|
132
|
+
|
120
133
|
- Create a VM and attach an existing stream (no auto-streams are created by the requestor):
|
121
134
|
|
122
135
|
```bash
|
@@ -129,8 +142,9 @@ poetry run golem vm create my-vm \
|
|
129
142
|
Environment (env prefix `GOLEM_REQUESTOR_`):
|
130
143
|
|
131
144
|
- `polygon_rpc_url` — EVM RPC URL (default L2 RPC)
|
132
|
-
- `stream_payment_address` — StreamPayment address (
|
133
|
-
- `glm_token_address` — Token address
|
145
|
+
- `stream_payment_address` — StreamPayment address (defaults from `contracts/deployments/l2.json`; overridden by provider info)
|
146
|
+
- `glm_token_address` — Token address (defaults from `contracts/deployments/l2.json`; zero address means native ETH)
|
147
|
+
- Optional override of deployments directory: set `GOLEM_DEPLOYMENTS_DIR` to a folder containing `l2.json`.
|
134
148
|
- `provider_eth_address` — optional dev helper; in production always use `/provider/info`
|
135
149
|
- `network` — Target network for discovery filtering: `testnet` (default) or `mainnet`
|
136
150
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "request-vm-on-golem"
|
3
|
-
version = "0.1.
|
3
|
+
version = "0.1.47"
|
4
4
|
description = "VM on Golem Requestor CLI - Create and manage virtual machines on the Golem Network"
|
5
5
|
authors = ["Phillip Jensen <phillip+vm-on-golem@golemgrid.com>"]
|
6
6
|
readme = "README.md"
|
@@ -21,6 +21,11 @@ packages = [
|
|
21
21
|
{ include = "requestor" }
|
22
22
|
]
|
23
23
|
|
24
|
+
# Include package data (deployment JSON)
|
25
|
+
include = [
|
26
|
+
{ path = "requestor/data/deployments/l2.json", format = "wheel" },
|
27
|
+
]
|
28
|
+
|
24
29
|
[tool.poetry.dependencies]
|
25
30
|
python = "^3.11"
|
26
31
|
click = "^8.0.1"
|
@@ -186,10 +186,11 @@ async def list_providers(cpu: Optional[int], memory: Optional[int], storage: Opt
|
|
186
186
|
@click.option('--memory', type=int, required=True, help='Memory in GB')
|
187
187
|
@click.option('--storage', type=int, required=True, help='Disk in GB')
|
188
188
|
@click.option('--stream-id', type=int, default=None, help='Optional StreamPayment stream id to fund this VM')
|
189
|
+
@click.option('--hours', type=int, default=1, help='If no stream-id is provided and payments are enabled, open a stream with this many hours of deposit (default 1)')
|
189
190
|
@click.option('--yes', is_flag=True, help='Do not prompt for confirmation')
|
190
191
|
@click.option('--network', type=click.Choice(['testnet', 'mainnet']), default=None, help='Override network for discovery during creation')
|
191
192
|
@async_command
|
192
|
-
async def create_vm(name: str, provider_id: str, cpu: int, memory: int, storage: int, stream_id: int | None, yes: bool, network: Optional[str] = None):
|
193
|
+
async def create_vm(name: str, provider_id: str, cpu: int, memory: int, storage: int, stream_id: int | None, hours: int, yes: bool, network: Optional[str] = None):
|
193
194
|
"""Create a new VM on a specific provider."""
|
194
195
|
try:
|
195
196
|
if network:
|
@@ -227,10 +228,7 @@ async def create_vm(name: str, provider_id: str, cpu: int, memory: int, storage:
|
|
227
228
|
if est_glm != '—':
|
228
229
|
price_str += f" (~{est_glm} GLM/mo)"
|
229
230
|
click.echo(click.style(f" 💵 Estimated Monthly Cost: {price_str}", fg='yellow', bold=True))
|
230
|
-
|
231
|
-
if not click.confirm("Proceed with VM creation?", default=True):
|
232
|
-
logger.warning("Creation cancelled by user")
|
233
|
-
return
|
231
|
+
# For streamlined UX, proceed without an interactive confirmation
|
234
232
|
|
235
233
|
# Setup SSH
|
236
234
|
ssh_service = SSHService(config.ssh_key_dir)
|
@@ -239,8 +237,46 @@ async def create_vm(name: str, provider_id: str, cpu: int, memory: int, storage:
|
|
239
237
|
# Initialize VM service
|
240
238
|
provider_url = config.get_provider_url(provider_ip)
|
241
239
|
async with ProviderClient(provider_url) as client:
|
240
|
+
# Fetch provider info if available (for preferred contract addresses); proceed regardless
|
241
|
+
info = None
|
242
|
+
try:
|
243
|
+
info = await client.get_provider_info()
|
244
|
+
except Exception:
|
245
|
+
info = None
|
246
|
+
# Always auto-open a stream when none provided (assume streaming required by default)
|
247
|
+
if stream_id is None:
|
248
|
+
# Compute rate from provider pricing
|
249
|
+
est = provider_service.compute_estimate(provider, (cpu, memory, storage))
|
250
|
+
if not est or est.get('glm_per_month') is None:
|
251
|
+
raise RequestorError('Provider requires streaming but does not advertise GLM pricing; cannot compute ratePerSecond')
|
252
|
+
glm_month = est['glm_per_month']
|
253
|
+
glm_per_second = float(glm_month) / (730.0 * 3600.0)
|
254
|
+
rate_per_second_wei = int(glm_per_second * (10**18))
|
255
|
+
deposit_wei = rate_per_second_wei * int(hours) * 3600
|
256
|
+
# Auto-fund via faucet if needed (testnets), then create stream
|
257
|
+
try:
|
258
|
+
from eth_account import Account
|
259
|
+
from ..security.faucet import L2FaucetService
|
260
|
+
acct = Account.from_key(config.ethereum_private_key)
|
261
|
+
faucet = L2FaucetService(config)
|
262
|
+
await faucet.request_funds(acct.address)
|
263
|
+
except Exception:
|
264
|
+
# Non-fatal; stream creation may still succeed if already funded
|
265
|
+
pass
|
266
|
+
# Open stream on-chain
|
267
|
+
from ..payments.blockchain_service import StreamPaymentClient, StreamPaymentConfig
|
268
|
+
spc = StreamPaymentConfig(
|
269
|
+
rpc_url=config.polygon_rpc_url,
|
270
|
+
contract_address=(info.get('stream_payment_address') if info else None) or config.stream_payment_address,
|
271
|
+
glm_token_address=(info.get('glm_token_address') if info else None) or config.glm_token_address,
|
272
|
+
private_key=config.ethereum_private_key,
|
273
|
+
)
|
274
|
+
sp_client = StreamPaymentClient(spc)
|
275
|
+
recipient = (info.get('provider_id') if info else None) or provider_id
|
276
|
+
stream_id = sp_client.create_stream(recipient, int(deposit_wei), int(rate_per_second_wei))
|
277
|
+
logger.success(f"Opened stream id={stream_id} (hours={hours})")
|
278
|
+
|
242
279
|
vm_service = VMService(db_service, ssh_service, client)
|
243
|
-
|
244
280
|
# Create VM
|
245
281
|
vm = await vm_service.create_vm(
|
246
282
|
name=name,
|
@@ -384,6 +420,97 @@ async def stream_topup(stream_id: int, glm: float | None, hours: int | None):
|
|
384
420
|
raise click.Abort()
|
385
421
|
|
386
422
|
|
423
|
+
@vm_stream.command('status')
|
424
|
+
@click.argument('name')
|
425
|
+
@click.option('--json', 'as_json', is_flag=True, help='Output in JSON format')
|
426
|
+
@async_command
|
427
|
+
async def stream_status(name: str, as_json: bool):
|
428
|
+
"""Show the payment stream status for a VM by name."""
|
429
|
+
try:
|
430
|
+
# Resolve VM and provider
|
431
|
+
vm = await db_service.get_vm(name)
|
432
|
+
if not vm:
|
433
|
+
raise RequestorError(f"VM '{name}' not found in local DB")
|
434
|
+
provider_url = config.get_provider_url(vm['provider_ip'])
|
435
|
+
async with ProviderClient(provider_url) as client:
|
436
|
+
status = await client.get_vm_stream_status(vm['vm_id'])
|
437
|
+
if as_json:
|
438
|
+
click.echo(json.dumps(status, indent=2))
|
439
|
+
return
|
440
|
+
# Pretty print
|
441
|
+
c = status.get('chain', {})
|
442
|
+
comp = status.get('computed', {})
|
443
|
+
click.echo("\n" + "─" * 60)
|
444
|
+
click.echo(click.style(f" 💸 Stream Status for VM: {name}", fg="blue", bold=True))
|
445
|
+
click.echo("─" * 60)
|
446
|
+
click.echo(f" Stream ID : {click.style(str(status.get('stream_id')), fg='cyan')}")
|
447
|
+
click.echo(f" Verified : {click.style(str(status.get('verified')), fg='green' if status.get('verified') else 'yellow')}")
|
448
|
+
click.echo(f" Reason : {status.get('reason')}")
|
449
|
+
click.echo(" On-chain :")
|
450
|
+
click.echo(f" recipient : {c.get('recipient')} ")
|
451
|
+
click.echo(f" startTime : {c.get('startTime')} stopTime: {c.get('stopTime')}")
|
452
|
+
click.echo(f" rate/second : {c.get('ratePerSecond')} deposit: {c.get('deposit')} withdrawn: {c.get('withdrawn')} halted: {c.get('halted')}")
|
453
|
+
click.echo(" Computed :")
|
454
|
+
click.echo(f" now : {comp.get('now')} remaining: {comp.get('remaining_seconds')}s")
|
455
|
+
click.echo(f" vested : {comp.get('vested_wei')} withdrawable: {comp.get('withdrawable_wei')}")
|
456
|
+
click.echo("─" * 60)
|
457
|
+
except Exception as e:
|
458
|
+
logger.error(f"Failed to fetch stream status: {e}")
|
459
|
+
raise click.Abort()
|
460
|
+
|
461
|
+
|
462
|
+
@vm_stream.command('inspect')
|
463
|
+
@click.option('--stream-id', type=int, required=True)
|
464
|
+
@click.option('--json', 'as_json', is_flag=True, help='Output in JSON format')
|
465
|
+
@async_command
|
466
|
+
async def stream_inspect(stream_id: int, as_json: bool):
|
467
|
+
"""Inspect a stream directly on-chain (no provider required)."""
|
468
|
+
try:
|
469
|
+
from web3 import Web3
|
470
|
+
from golem_streaming_abi import STREAM_PAYMENT_ABI
|
471
|
+
w3 = Web3(Web3.HTTPProvider(config.polygon_rpc_url))
|
472
|
+
contract = w3.eth.contract(address=Web3.to_checksum_address(config.stream_payment_address), abi=STREAM_PAYMENT_ABI)
|
473
|
+
token, sender, recipient, startTime, stopTime, ratePerSecond, deposit, withdrawn, halted = contract.functions.streams(int(stream_id)).call()
|
474
|
+
now = int(w3.eth.get_block('latest')['timestamp'])
|
475
|
+
vested = max(min(now, int(stopTime)) - int(startTime), 0) * int(ratePerSecond)
|
476
|
+
withdrawable = max(int(vested) - int(withdrawn), 0)
|
477
|
+
remaining = max(int(stopTime) - now, 0)
|
478
|
+
out = {
|
479
|
+
"stream_id": int(stream_id),
|
480
|
+
"chain": {
|
481
|
+
"token": token,
|
482
|
+
"sender": sender,
|
483
|
+
"recipient": recipient,
|
484
|
+
"startTime": int(startTime),
|
485
|
+
"stopTime": int(stopTime),
|
486
|
+
"ratePerSecond": int(ratePerSecond),
|
487
|
+
"deposit": int(deposit),
|
488
|
+
"withdrawn": int(withdrawn),
|
489
|
+
"halted": bool(halted),
|
490
|
+
},
|
491
|
+
"computed": {
|
492
|
+
"now": now,
|
493
|
+
"remaining_seconds": remaining,
|
494
|
+
"vested_wei": int(vested),
|
495
|
+
"withdrawable_wei": int(withdrawable),
|
496
|
+
}
|
497
|
+
}
|
498
|
+
if as_json:
|
499
|
+
click.echo(json.dumps(out, indent=2))
|
500
|
+
else:
|
501
|
+
click.echo("\n" + "─" * 60)
|
502
|
+
click.echo(click.style(f" 🔎 On-chain Stream Inspect: {stream_id}", fg="blue", bold=True))
|
503
|
+
click.echo("─" * 60)
|
504
|
+
click.echo(f" recipient : {recipient}")
|
505
|
+
click.echo(f" startTime : {int(startTime)} stopTime: {int(stopTime)} now: {now} remaining: {remaining}s")
|
506
|
+
click.echo(f" rate/second : {int(ratePerSecond)} deposit: {int(deposit)} withdrawn: {int(withdrawn)} halted: {bool(halted)}")
|
507
|
+
click.echo(f" vested : {int(vested)} withdrawable: {int(withdrawable)}")
|
508
|
+
click.echo("─" * 60)
|
509
|
+
except Exception as e:
|
510
|
+
logger.error(f"Failed to inspect stream: {e}")
|
511
|
+
raise click.Abort()
|
512
|
+
|
513
|
+
|
387
514
|
@cli.group()
|
388
515
|
def wallet():
|
389
516
|
"""Wallet utilities (funding, balance)."""
|
@@ -106,12 +106,12 @@ class RequestorConfig(BaseSettings):
|
|
106
106
|
description="EVM RPC URL for streaming payments (L2 by default)"
|
107
107
|
)
|
108
108
|
stream_payment_address: str = Field(
|
109
|
-
default="
|
110
|
-
description="Deployed StreamPayment contract address"
|
109
|
+
default="",
|
110
|
+
description="Deployed StreamPayment contract address (defaults to contracts/deployments/l2.json)"
|
111
111
|
)
|
112
112
|
glm_token_address: str = Field(
|
113
|
-
default="
|
114
|
-
description="Token address (0x0 means native ETH)"
|
113
|
+
default="",
|
114
|
+
description="Token address (0x0 means native ETH). Defaults from l2.json"
|
115
115
|
)
|
116
116
|
# Faucet settings (L2 payments)
|
117
117
|
l2_faucet_url: str = Field(
|
@@ -145,6 +145,52 @@ class RequestorConfig(BaseSettings):
|
|
145
145
|
return os.environ[key]
|
146
146
|
return v
|
147
147
|
|
148
|
+
@staticmethod
|
149
|
+
def _load_l2_deployment() -> tuple[str | None, str | None]:
|
150
|
+
try:
|
151
|
+
base = os.environ.get("GOLEM_DEPLOYMENTS_DIR")
|
152
|
+
if base:
|
153
|
+
path = Path(base) / "l2.json"
|
154
|
+
else:
|
155
|
+
# repo root assumption: ../../ relative to this file
|
156
|
+
path = Path(__file__).resolve().parents[2] / "contracts" / "deployments" / "l2.json"
|
157
|
+
if not path.exists():
|
158
|
+
# Try package resource fallback
|
159
|
+
try:
|
160
|
+
import importlib.resources as ir
|
161
|
+
with ir.files("requestor.data.deployments").joinpath("l2.json").open("r") as fh: # type: ignore[attr-defined]
|
162
|
+
import json as _json
|
163
|
+
data = _json.load(fh)
|
164
|
+
except Exception:
|
165
|
+
return None, None
|
166
|
+
else:
|
167
|
+
import json as _json
|
168
|
+
data = _json.loads(path.read_text())
|
169
|
+
sp = data.get("StreamPayment", {})
|
170
|
+
addr = sp.get("address")
|
171
|
+
token = sp.get("glmToken")
|
172
|
+
if isinstance(addr, str) and addr:
|
173
|
+
return addr, token or "0x0000000000000000000000000000000000000000"
|
174
|
+
except Exception:
|
175
|
+
pass
|
176
|
+
return None, None
|
177
|
+
|
178
|
+
@field_validator("stream_payment_address", mode='before')
|
179
|
+
@classmethod
|
180
|
+
def default_stream_addr(cls, v: str) -> str:
|
181
|
+
if v:
|
182
|
+
return v
|
183
|
+
addr, _ = RequestorConfig._load_l2_deployment()
|
184
|
+
return addr or "0x0000000000000000000000000000000000000000"
|
185
|
+
|
186
|
+
@field_validator("glm_token_address", mode='before')
|
187
|
+
@classmethod
|
188
|
+
def default_token_addr(cls, v: str) -> str:
|
189
|
+
if v:
|
190
|
+
return v
|
191
|
+
_, token = RequestorConfig._load_l2_deployment()
|
192
|
+
return token or "0x0000000000000000000000000000000000000000"
|
193
|
+
|
148
194
|
# Base Directory
|
149
195
|
base_dir: Path = Field(
|
150
196
|
default_factory=lambda: Path.home() / ".golem" / "requestor",
|
@@ -103,3 +103,13 @@ class ProviderClient:
|
|
103
103
|
error_text = await response.text()
|
104
104
|
raise Exception(f"Failed to get VM access info: {error_text}")
|
105
105
|
return await response.json()
|
106
|
+
|
107
|
+
async def get_vm_stream_status(self, vm_id: str) -> Dict:
|
108
|
+
"""Get on-chain stream status for a VM from provider."""
|
109
|
+
async with self.session.get(
|
110
|
+
f"{self.provider_url}/api/v1/vms/{vm_id}/stream"
|
111
|
+
) as response:
|
112
|
+
if not response.ok:
|
113
|
+
error_text = await response.text()
|
114
|
+
raise Exception(f"Failed to get VM stream status: {error_text}")
|
115
|
+
return await response.json()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/payments/blockchain_service.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/services/database_service.py
RENAMED
File without changes
|
{request_vm_on_golem-0.1.45 → request_vm_on_golem-0.1.47}/requestor/services/provider_service.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|