Skip to content

Commit 343a563

Browse files
Merge pull request #68 from TomboScaleComputing/api-put-json2
api module: Add support for PUT methods
2 parents 4d7d436 + e67e37d commit 343a563

File tree

4 files changed

+132
-6
lines changed

4 files changed

+132
-6
lines changed

examples/virtual_disk.yml

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
- name: Upload a virtual disk image from http link to HyperCore
3+
hosts: localhost
4+
connection: local
5+
gather_facts: false
6+
vars:
7+
image_url: https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img
8+
# image_url: https://github.com/ddemlow/RestAPIExamples/blob/master/ubuntu18_04-cloud-init/ubuntu18cloudimage.qcow2
9+
image_filename: "{{ image_url | split('/') | last }}"
10+
image_remove_old: false
11+
sc_host: https://10.100.20.38 #TODO
12+
sc_username: admin
13+
sc_password: admin
14+
15+
tasks:
16+
# # ------------------------------------------------------
17+
- name: Download Virtual Disk {{ image_filename }} from URL
18+
ansible.builtin.get_url: #TODO: what if file doesn't download completely?
19+
url: "{{ image_url }}"
20+
dest: /tmp/{{ image_filename }}
21+
22+
- name: Get the Virtual Disk size
23+
ansible.builtin.stat:
24+
path: /tmp/{{ image_filename }}
25+
register: disk_file_info
26+
27+
#TODO
28+
# - name: (Optionally) remove existing Virtual Disk {{ image_filename }} from HyperCore
29+
# scale_computing.hypercore.api:
30+
# action: get
31+
# cluster_instance:
32+
# host: "{{ sc_host }}"
33+
# username: "{{ sc_username }}"
34+
# password: "{{ sc_password }}"
35+
# endpoint: "/rest/v1/VirtualDisk"
36+
# register: virtualDiskResult
37+
38+
# ------------------------------------------------------
39+
- name: "Upload Virtual Disk {{ image_filename }} to HyperCore"
40+
scale_computing.hypercore.api:
41+
action: put
42+
cluster_instance:
43+
host: "{{ sc_host }}"
44+
username: "{{ sc_username }}"
45+
password: "{{ sc_password }}"
46+
endpoint: "/rest/v1/VirtualDisk/upload"
47+
data:
48+
filename: "{{ image_filename }}"
49+
filesize: "{{ disk_file_info.stat.size }}"
50+
source: /tmp/{{ image_filename }}
51+
register: uploadResult
52+
53+
# ------------------------------------------------------
54+
- name: Get Information About the uploaded Virtual Disk in HyperCore
55+
scale_computing.hypercore.api:
56+
action: get
57+
cluster_instance:
58+
host: "{{ sc_host }}"
59+
username: "{{ sc_username }}"
60+
password: "{{ sc_password }}"
61+
endpoint: "/rest/v1/VirtualDisk/{{ uploadResult.record.createdUUID }}"
62+
register: result
63+
64+
- name: Show uploaded disk info
65+
debug:
66+
var: result.record[0]

plugins/module_utils/rest_client.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from . import errors
99
from . import utils
10+
import json
1011

1112
__metaclass__ = type
1213

@@ -84,27 +85,30 @@ def put_record(
8485
endpoint,
8586
payload,
8687
check_mode,
88+
query=None,
8789
timeout=None,
8890
binary_data=None,
8991
headers=None,
9092
):
9193
# Method put doesn't support check mode # IT ACTUALLY DOES
9294
if check_mode:
9395
return None
94-
# Only /rest/v1/ISO/[uuid}/data is using put, which doesn't return anything.
95-
# self.client.put on this endpoint returns None.
9696
try:
9797
response = self.client.put(
9898
endpoint,
9999
data=payload,
100-
query=_query(),
100+
query=query,
101101
timeout=timeout,
102102
binary_data=binary_data,
103103
headers=headers,
104104
)
105105
except TimeoutError as e:
106106
raise errors.ScaleComputingError(f"Request timed out: {e}")
107-
return response
107+
108+
try:
109+
return response.json
110+
except errors.ScaleComputingError as e:
111+
return response
108112

109113

110114
class CachedRestClient(RestClient):

plugins/modules/api.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
- Tjaž Eržen (@tjazsch)
1717
short_description: API interaction with Scale Computing HyperCore
1818
description:
19-
- Perform a C(GET), C(POST), C(PATCH) or C(DELETE) request on resource(s) from the given endpoint.
19+
- Perform a C(GET), C(POST), C(PATCH), C(DELETE), or C(PUT) request on resource(s) from the given endpoint.
2020
The api module can be used to perform raw API calls whenever there is no
2121
suitable concrete module or role implementation for a specific task.
2222
version_added: 1.0.0
@@ -36,6 +36,7 @@
3636
- delete
3737
- get
3838
- post_list
39+
- put
3940
data:
4041
type: dict
4142
description:
@@ -239,6 +240,34 @@ def delete_record(module, rest_client):
239240
return True, task_tag
240241
return False, dict()
241242

243+
"""
244+
PUT_TIMEOUT_TIME was copied from the iso module for ISO data upload.
245+
Currently, assume we have 4.7 GB ISO and speed 1 MB/s -> 4700 seconds.
246+
Rounded to 3600.
247+
248+
TODO: compute it from expected min upload speed and file size.
249+
Even better, try to detect stalled uploads and terminate if no data was transmitted for more than N seconds.
250+
Yum/dnf complain with error "Operation too slow. Less than 1000 bytes/sec transferred the last 30 seconds"
251+
in such case.
252+
"""
253+
PUT_TIMEOUT_TIME = 3600
254+
255+
def put_record(module, rest_client):
256+
with open(module.params["source"], "rb") as source_file:
257+
result = rest_client.put_record(
258+
endpoint=module.params["endpoint"],
259+
payload=None,
260+
check_mode=module.check_mode,
261+
query=module.params["data"],
262+
timeout=PUT_TIMEOUT_TIME,
263+
binary_data=source_file,
264+
headers={
265+
"Content-Type": "application/octet-stream",
266+
"Accept": "application/json",
267+
}
268+
)
269+
return True, result
270+
242271

243272
def get_records(module, rest_client):
244273
records = rest_client.list_records(
@@ -258,6 +287,8 @@ def run(module, rest_client):
258287
return post_list_record(module, rest_client)
259288
elif action == "get": # GET method
260289
return get_records(module, rest_client)
290+
elif action == "put": # PUT method
291+
return put_record(module, rest_client)
261292
return delete_record(module, rest_client) # DELETE methodx
262293

263294

@@ -271,13 +302,16 @@ def main():
271302
),
272303
action=dict(
273304
type="str",
274-
choices=["post", "patch", "delete", "get", "post_list"],
305+
choices=["post", "patch", "delete", "get", "post_list", "put"],
275306
required=True,
276307
),
277308
endpoint=dict(
278309
type="str",
279310
required=True,
280311
),
312+
source=dict(
313+
type="str",
314+
),
281315
),
282316
)
283317

tests/unit/plugins/modules/test_api.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,28 @@ def test_get_method_record_absent(self, create_module, rest_client):
7575
assert result == (False, [])
7676

7777

78+
class TestPutMethod:
79+
def test_put_method(self, create_module, rest_client):
80+
# TODO: Put method hasn't been implemented yet, so tests still have to be written.
81+
# Harcoding value for now.
82+
module = create_module(
83+
params=dict(
84+
cluster_instance=dict(
85+
host="https://0.0.0.0",
86+
username="admin",
87+
password="admin",
88+
),
89+
action="put",
90+
endpoint="/rest/v1/VirDomain",
91+
unique_id="id",
92+
data=dict(),
93+
)
94+
)
95+
96+
result = api.put_record(module, rest_client)
97+
assert result == (-1, -1, -1)
98+
99+
78100
class TestDeleteRecord:
79101
def test_delete_method_record_present(self, create_module, rest_client, task_wait):
80102
module = create_module(

0 commit comments

Comments
 (0)