From 95073698f927284c3bf276786e339b4352616138 Mon Sep 17 00:00:00 2001 From: Alex Brett Date: Wed, 16 Oct 2024 12:33:05 +0000 Subject: [PATCH] Update wire-protocol.md to have working Python3 code examples The document was updated in f785993f244c0b2234f68338a3478d908bc4a9f4 to use Python 3, however the examples were left unchanged. In Python 3, `xmlrpclib` does not exist, it has been replaced by the `xmlrpc` module (specifically in this case `xmlrpclib.Server` has been replaced by `xmlrpc.client.ServerProxy`). Additionally, the `python-jsonrpc` package has not been ported to Python 3, and as such is unavailable. There is no direct replacement, however a combination of the `jsonrpcclient` package and the `requests` packages provide a reasonable solution. This commit updates the examples appropriately so they function in Python 3. Signed-off-by: Alex Brett --- ocaml/doc/wire-protocol.md | 71 +++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/ocaml/doc/wire-protocol.md b/ocaml/doc/wire-protocol.md index 155a27b23e0..e266afa3a6e 100644 --- a/ocaml/doc/wire-protocol.md +++ b/ocaml/doc/wire-protocol.md @@ -469,12 +469,21 @@ $ python3 ### Using the XML-RPC Protocol -Import the library `xmlrpclib` and create a +Import the library `xmlrpc.client` and create a python object referencing the remote server as shown below: ```python ->>> import xmlrpclib ->>> xen = xmlrpclib.Server("https://localhost:443") +>>> import xmlrpc.client +>>> xen = xmlrpc.client.ServerProxy("https://localhost:443") +``` + +Note that you may need to disable SSL certificate validation to establish the +connection, this can be done as follows: + +```python +>>> import ssl +>>> ctx = ssl._create_unverified_context() +>>> xen = xmlrpc.client.ServerProxy("https://localhost:443", context=ctx) ``` Acquire a session reference by logging in with a username and password; the @@ -555,27 +564,38 @@ To retrieve all the VM records in a single call: ### Using the JSON-RPC Protocol -For this example we are making use of the package `python-jsonrpc` due to its -simplicity, although other packages can also be used. +For this example we are making use of the package `jsonrpcclient` and the +`requests` library due to their simplicity, although other packages can also be +used. -First, import the library `pyjsonrpc` and create the object referencing the -remote server as follows: +First, import the `requests` and `jsonrpcclient` libraries: ```python ->>> import pyjsonrpc ->>> client = pyjsonrpc.HttpClient(url = "https://localhost/jsonrpc:443") +>>> import requests +>>> import jsonrpcclient ``` -Acquire a session reference by logging in with a username and password; the -library `pyjsonrpc` returns the response's `result` member, which is the session -reference: +Now we construct a utility method to make using these libraries easier: + +```python +>>> def jsonrpccall(method, params): +... r = requests.post("https://localhost:443/jsonrpc", +... json=jsonrpcclient.request(method, params=params), +... verify=False) +... p = jsonrpcclient.parse(r.json()) +... if isinstance(p, jsonrpcclient.Ok): +... return p.result +... raise Exception(p.message, p.data) +``` + +Acquire a session reference by logging in with a username and password: ```python ->>> session = client.call("session.login_with_password", -... "user", "passwd", "version", "originator") +>>> session = jsonrpccall("session.login_with_password", +... ("user", "password", "version", "originator")) ``` -`pyjsonrpc` uses the JSON-RPC protocol v2.0, so this is what the serialized +`jsonrpcclient` uses the JSON-RPC protocol v2.0, so this is what the serialized request looks like: ```json @@ -591,7 +611,7 @@ Next, the user may acquire a list of all the VMs known to the system (note the call takes the session reference as the only parameter): ```python ->>> all_vms = client.call("VM.get_all", session) +>>> all_vms = jsonrpccall("VM.get_all", (session,)) >>> all_vms ['OpaqueRef:1', 'OpaqueRef:2', 'OpaqueRef:3', 'OpaqueRef:4' ] ``` @@ -603,22 +623,19 @@ find the subset of template VMs using a command like the following: ```python >>> all_templates = filter( -... lambda x: client.call("VM.get_is_a_template", session, x), - all_vms) +... lambda x: jsonrpccall("VM.get_is_a_template", (session, x)), +... all_vms) ``` Once a reference to a VM has been acquired, a lifecycle operation may be invoked: ```python ->>> from pyjsonrpc import JsonRpcError >>> try: -... client.call("VM.start", session, all_templates[0], False, False) -... except JsonRpcError as e: -... e.message -... e.data +... jsonrpccall("VM.start", (session, next(all_templates), False, False)) +... except Exception as e: +... e ... -'VM_IS_TEMPLATE' -[ 'OpaqueRef:1', 'start' ] +Exception('VM_IS_TEMPLATE', ['OpaqueRef:1', 'start']) ``` In this case the `start` message has been rejected because the VM is @@ -629,7 +646,7 @@ Rather than querying fields individually, whole _records_ may be returned at onc To retrieve the record of a single object as a python dictionary: ```python ->>> record = client.call("VM.get_record", session, all_templates[0]) +>>> record = jsonrpccall("VM.get_record", (session, next(all_templates))) >>> record['power_state'] 'Halted' >>> record['name_label'] @@ -639,7 +656,7 @@ To retrieve the record of a single object as a python dictionary: To retrieve all the VM records in a single call: ```python ->>> records = client.call("VM.get_all_records", session) +>>> records = jsonrpccall("VM.get_all_records", (session,)) >>> records.keys() ['OpaqueRef:1', 'OpaqueRef:2', 'OpaqueRef:3', 'OpaqueRef:4' ] >>> records['OpaqueRef:1']['name_label']