Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
b80c7a1
fix: Using 2 structs can remove some is_none Some checks
Dec 11, 2024
84a51db
feat: add tracing for logs
Dec 11, 2024
53f9295
feat: Add tracing to example
Dec 11, 2024
a08c509
style: remove dead code
Dec 11, 2024
df2ac14
style: Remove unused imports
Dec 11, 2024
b2dbb21
style: run cargo fmt
Dec 11, 2024
613c798
style: run black
Dec 11, 2024
cd2f909
refactor: Reduce 1 level of nesting with `and_then`
Dec 12, 2024
bd78a4c
chore: add log line when processing buffer
Dec 13, 2024
b6df1e4
fix: @rgooch says this shouldnt happen and is handle in next case
Dec 13, 2024
386166e
feat: Allow streaming responses to python
Dec 13, 2024
7e7147a
chore: Examples for streaming responses
Dec 13, 2024
7739f58
feat: Allow passing a callback from python
Dec 13, 2024
90dd80b
feat: switch to a more idiomatic FrameRead *LineCodec
Dec 14, 2024
02560b1
chore: Make examples use env vars for flexibility
Dec 14, 2024
7f0e6bc
chore: loosen bounds and bump version for api change
Dec 14, 2024
4d2f7da
chore: Abstract to try to add test seam
Dec 14, 2024
b189c34
test: Add first test, and timeout so we dont hang on bad test
Dec 14, 2024
d6df389
chore: Remove unused old receive_message
Dec 14, 2024
a8706bc
chore: update pyo3
Dec 14, 2024
380927f
fix: restore streaming with timeout
Dec 15, 2024
3830d54
fix: Dont overread the buffer when expecting empty
Dec 16, 2024
9416beb
feat: Add helpers for reading back empty ack
Dec 16, 2024
d85cc5a
feat: Add rudimentary request_reply api and example
Dec 16, 2024
882a152
feat: Expose *_and_check methods to python
Dec 16, 2024
11eef65
docs: Better preamble for examples
Dec 16, 2024
fda0805
wip: try to make Conn api
Dec 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 28 additions & 9 deletions rust/lib/srpc/client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,36 +1,55 @@
[package]
name = "srpc_client"
version = "0.1.1"
version = "0.2.0"
edition = "2021"

[lib]
name = "srpc_client"
crate-type = ["cdylib", "rlib"]

[dependencies]
tokio = { version = "1.0", features = ["full"] }
async-trait = "0.1"
bytes = "1"
futures = "0.3"
tokio = { version = "1", features = ["full"] }
openssl = "0.10"
serde_json = "1.0"
serde = {version = "1", features = ["derive"]}
serde_json = "1"
tokio-openssl = "0.6"
tokio-util = { version = "0.7", features = ["codec"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }

[dependencies.pyo3]
version = "0.18"
version = "0.23"
features = ["extension-module"]
optional = true

[dependencies.pyo3-asyncio]
version = "0.18"
features = ["tokio-runtime"]
[dependencies.pyo3-async-runtimes]
version = "0.23"
features = ["attributes", "tokio-runtime"]
optional = true

[features]
default = []
python = ["pyo3", "pyo3-asyncio"]
python = ["pyo3", "pyo3-async-runtimes"]

[[example]]
name = "rust_client_example"
path = "examples/rust_client_example.rs"
required-features = []

[[example]]
name = "rust_client_example2"
path = "examples/rust_client_example2.rs"
required-features = []

[[example]]
name = "rust_client_example3"
path = "examples/rust_client_example3.rs"
required-features = []

[dev-dependencies]
tokio = { version = "1.0", features = ["full", "macros"] }
rstest = "0.23.0"
test-log = { version = "0.2.16", features = ["trace", "color"] }
tokio = { version = "1", features = ["full", "macros", "test-util"] }
33 changes: 23 additions & 10 deletions rust/lib/srpc/client/examples/python_client_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,33 @@
This example demonstrates how to use the srpc_client Python bindings.

To run this example:
1. Build the Rust library: maturin build --features python
2. Install the wheel: pip install target/wheels/srpc_client-*.whl
3. Run this script: python examples/python_client_example.py
1. Build and install the Rust python library: maturin develop --features python
3. Run this script:
RUST_LOG=trace \
EXAMPLE_1_SRPC_SERVER_HOST=<host> \
EXAMPLE_1_SRPC_SERVER_PORT=<port> \
EXAMPLE_1_SRPC_SERVER_ENPOINT=<srpc endpoint> \
EXAMPLE_1_SRPC_SERVER_CERT=<path to .cert> \
EXAMPLE_1_SRPC_SERVER_KEY=<path to .key> \
python examples/python_client_example.py
"""

import asyncio
import json
from srpc_client import SrpcClient
import os
from srpc_client import SrpcClientConfig


async def main():
client = SrpcClient("<Hostname or IP of hypervisor>", 6976, "/_SRPC_/TLS/JSON", "<Path to Keymaster Certificate file>", "<Path to Keymaster Key file>")

await client.connect()
client = SrpcClientConfig(
os.environ["EXAMPLE_1_SRPC_SERVER_HOST"],
int(os.environ["EXAMPLE_1_SRPC_SERVER_PORT"]),
os.environ["EXAMPLE_1_SRPC_SERVER_ENPOINT"],
os.environ["EXAMPLE_1_SRPC_SERVER_CERT"],
os.environ["EXAMPLE_1_SRPC_SERVER_KEY"],
)

client = await client.connect()
print("Connected to server")

message = "Hypervisor.StartVm\n"
Expand All @@ -25,9 +39,7 @@ async def main():
for response in responses:
print(f"Received response: {response}")

json_payload = {
"IpAddress": "<IP Address of VM>"
}
json_payload = {"IpAddress": "<IP Address of VM>"}
json_string = json.dumps(json_payload)
await client.send_json(json_string)
print(f"Sent JSON payload: {json_payload}")
Expand All @@ -36,5 +48,6 @@ async def main():
for json_response in json_responses:
print(f"Received JSON response: {json.loads(json_response)}")


if __name__ == "__main__":
asyncio.run(main())
57 changes: 57 additions & 0 deletions rust/lib/srpc/client/examples/python_client_example2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""
This example demonstrates how to use the srpc_client Python bindings.

To run this example:
1. Build and install the Rust python library: maturin develop --features python
3. Run this script:
RUST_LOG=trace \
EXAMPLE_2_SRPC_SERVER_HOST=<host> \
EXAMPLE_2_SRPC_SERVER_PORT=<port> \
EXAMPLE_2_SRPC_SERVER_ENPOINT=<srpc endpoint> \
EXAMPLE_2_SRPC_SERVER_CERT=<path to .cert> \
EXAMPLE_2_SRPC_SERVER_KEY=<path to .key> \
python examples/python_client_example2.py
"""

import asyncio
import json
import os
from srpc_client import SrpcClientConfig


async def main():
print("Starting client..")

# Create a new ClientConfig instance
client = SrpcClientConfig(
os.environ["EXAMPLE_2_SRPC_SERVER_HOST"],
int(os.environ["EXAMPLE_2_SRPC_SERVER_PORT"]),
os.environ["EXAMPLE_2_SRPC_SERVER_ENPOINT"],
os.environ["EXAMPLE_2_SRPC_SERVER_CERT"],
os.environ["EXAMPLE_2_SRPC_SERVER_KEY"],
)

# Connect to the server
client = await client.connect()
print("Connected to server")

# Send a message
message = "Hypervisor.GetUpdates\n"
print(f"Sending message: {message}")
await client.send_message(message)
print(f"Sent message: {message}")

# Receive an empty response
print("Waiting for empty string response...")
responses = await client.receive_message(expect_empty=True, should_continue=False)
async for response in responses:
print(f"Received response: {response}")

# Receive responses
responses = await client.receive_json_cb(should_continue=lambda _: True)
async for response in responses:
print(f"Received response: {json.loads(response)}")


if __name__ == "__main__":
asyncio.run(main())
95 changes: 95 additions & 0 deletions rust/lib/srpc/client/examples/python_client_example3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"""
This example demonstrates how to use the srpc_client Python bindings.

To run this example:
1. Build and install the Rust python library: maturin develop --features python
3. Run this script:
RUST_LOG=trace \
EXAMPLE_3_SRPC_SERVER_HOST=<host> \
EXAMPLE_3_SRPC_SERVER_PORT=<port> \
EXAMPLE_3_SRPC_SERVER_ENPOINT=<srpc endpoint> \
EXAMPLE_3_SRPC_SERVER_CERT=<path to .cert> \
EXAMPLE_3_SRPC_SERVER_KEY=<path to .key> \
python examples/python_client_example3.py
"""

import asyncio
import json
import os
from srpc_client import SrpcClientConfig


async def main():
print("Starting client..")

# Create a new ClientConfig instance
client = SrpcClientConfig(
os.environ["EXAMPLE_3_SRPC_SERVER_HOST"],
int(os.environ["EXAMPLE_3_SRPC_SERVER_PORT"]),
os.environ["EXAMPLE_3_SRPC_SERVER_ENPOINT"],
os.environ["EXAMPLE_3_SRPC_SERVER_CERT"],
os.environ["EXAMPLE_3_SRPC_SERVER_KEY"],
)

# Connect to the server
client = await client.connect()
print("Connected to server")

message = "Hypervisor.ListVMs\n"

# Send a message
print(f"Sending message: {message}")
await client.send_message(message)
print(f"Sent message: {message}")

# Receive an empty response
print("Waiting for empty string response...")
responses = await client.receive_message(expect_empty=True, should_continue=False)
async for response in responses:
print(f"Received response: {response}")

# Send a JSON message
payload = json.dumps(
{
"IgnoreStateMask": 0,
"OwnerGroups": [],
"OwnerUsers": [],
"Sort": True,
"VmTagsToMatch": {},
}
)
print(f"Sending payload: {payload}")
await client.send_json(payload)
print(f"Sent payload: {payload}")

# Receive an empty response
print("Waiting for empty string response for payload...")
responses = await client.receive_message(expect_empty=True, should_continue=False)
async for response in responses:
print(f"Received response: {response}")

# Receive responses
print("Waiting for response...")
responses = await client.receive_json_cb(should_continue=lambda _: False)
async for response in responses:
print(f"Received response: {json.loads(response)}")

# Use RequestReply
print(f"Sending request_reply: {message}")
res = await client.request_reply(
message,
json.dumps(
{
"IgnoreStateMask": 0,
"OwnerGroups": [],
"OwnerUsers": [],
"Sort": True,
"VmTagsToMatch": {},
}
),
)
print(f"Sent request_reply: {message}, got reply: {res}")


if __name__ == "__main__":
asyncio.run(main())
54 changes: 54 additions & 0 deletions rust/lib/srpc/client/examples/python_client_example4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""
This example demonstrates how to use the srpc_client Python bindings.

To run this example:
1. Build and install the Rust python library: maturin develop --features python
3. Run this script:
RUST_LOG=trace \
EXAMPLE_4_SRPC_SERVER_HOST=<host> \
EXAMPLE_4_SRPC_SERVER_PORT=<port> \
EXAMPLE_4_SRPC_SERVER_ENPOINT=<srpc endpoint> \
EXAMPLE_4_SRPC_SERVER_CERT=<path to .cert> \
EXAMPLE_4_SRPC_SERVER_KEY=<path to .key> \
python examples/python_client_example4.py
"""

import asyncio
import json
import os
from srpc_client import SrpcClientConfig


async def main():
print("Starting client..")

# Create a new ClientConfig instance
client = SrpcClientConfig(
os.environ["EXAMPLE_4_SRPC_SERVER_HOST"],
int(os.environ["EXAMPLE_4_SRPC_SERVER_PORT"]),
os.environ["EXAMPLE_4_SRPC_SERVER_ENPOINT"],
os.environ["EXAMPLE_4_SRPC_SERVER_CERT"],
os.environ["EXAMPLE_4_SRPC_SERVER_KEY"],
)

# Connect to the server
client = await client.connect()
print("Connected to server")

# Send a message
message = "Hypervisor.GetUpdates\n"
print(f"Calling server with message: {message}")
conn = await client.call(message)
response = await conn.decode()
print(f"Received response: {json.loads(response)}")
await conn.close()

print(f"Calling server with message again: {message}")
conn2 = await client.call(message)
response = await conn2.decode()
print(f"Received response: {json.loads(response)}")
await conn2.close()


if __name__ == "__main__":
asyncio.run(main())
Loading