Skip to content

Commit 8835ed9

Browse files
authored
Merge pull request #3 from UpCloudLtd/master
Add changes
2 parents c2149d5 + abcc5f9 commit 8835ed9

27 files changed

+655
-58
lines changed

README.md

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,6 @@ python setup.py install
5050
* Manage firewall (on/off and individual rules)
5151
* since 0.2: full management of firewall rules
5252

53-
**TODO:**
54-
* Cloning of storages
55-
* Full management of special storage types:
56-
* CDROMs, custom OS templates
57-
* (custom templates can already be cloned to a disk via UUID)
58-
* Full management of backups (instant and scheduled)
59-
6053
**Changelog:**
6154
* See the [Releases page](https://github.com/UpCloudLtd/upcloud-python-api/releases)
6255

@@ -240,21 +233,21 @@ Set up environment and install dependencies:
240233
# run at project root, python3 and virtualenv must be installed
241234
virtualenv ENV
242235
source ENV/bin/activate
243-
pip install -r requirements.txt
236+
pip install -r requirements.txt or pip install -r requirements-dev.txt if changes to the api need to be made
244237
```
245238

246239
Install the package in editable mode, as mentioned in
247-
[https://pytest.org/latest/goodpractises.html](https://pytest.org/latest/goodpractises.html)
240+
[https://docs.pytest.org/en/stable/goodpractices.html](https://docs.pytest.org/en/stable/goodpractices.html)
248241

249242
```python
250243
# run at project root
251244
pip install -e .
252245
```
253246

254-
Tests located in `project_root/tests/` directory. Run with:
247+
Tests located in `project_root/test/` directory. Run with:
255248

256249
```python
257-
py.test tests/
250+
py.test test/
258251
```
259252

260253
To test against all supported python versions, run:

docs/Storage.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,64 @@ Warning: data loss is permanent.
8181
storage.destroy()
8282

8383
```
84+
85+
86+
## Clone
87+
88+
Clone the storage using StorageManager.
89+
Returns an object based on the API's response.
90+
Method requires title and zone to be passed while tier is optional.
91+
92+
```python
93+
94+
storage_clone = storage.clone(title='title of storage clone', zone=ZONE.Helsinki, tier=None)
95+
96+
```
97+
98+
99+
## Cancel clone operation
100+
101+
Cancels a running cloning operation and deletes the incomplete copy using StorageManager.
102+
Needs to be called from the cloned storage (object returned by clone operation) and not the storage that is being cloned.
103+
104+
```python
105+
106+
storage_clone.cancel_cloning()
107+
108+
```
109+
110+
111+
## Create backup
112+
113+
Creates a point-in-time backup of a storage resource using StorageManager.
114+
Method requires title to be passed.
115+
116+
```python
117+
118+
storage_backup = storage.create_backup('Backup title')
119+
120+
```
121+
122+
123+
## Restore backup
124+
125+
Restores the origin storage with data from the specified backup storage using StorageManager.
126+
Must be called from a storage object created by create_backup and not the original one.
127+
128+
```python
129+
130+
storage_backup.restore_backup()
131+
132+
```
133+
134+
135+
## Templatize storage
136+
137+
Creates an exact copy of an existing storage resource which can be used as a template for creating new servers using StorageManager.
138+
Method requires title to be passed.
139+
140+
```python
141+
142+
storage.templatize('Template title')
143+
144+
```

docs/server-mixin.md

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ manager.method()
2020
```python
2121
def get_servers(self, populate=False):
2222
"""
23-
Returns a list of (populated or unpopulated) Server instances.
23+
Returns a list of (populated or unpopulated) Server instances.
2424
Populate = False (default) => 1 API request, returns unpopulated Server instances.
2525
Populate = True => Does 1 + n API requests (n = # of servers), returns populated Server instances.
2626
"""
@@ -36,14 +36,14 @@ def get_server(self, UUID):
3636
```python
3737
def create_server(self, server):
3838
"""
39-
Creates a server and its storages based on a (locally created) Server object.
39+
Creates a server and its storages based on a (locally created) Server object.
4040
Populates the given Server instance with the API response.
4141
4242
Example:
4343
server1 = Server( core_number = 1,
44-
memory_amount = 1024,
45-
hostname = "my.example.1",
46-
zone = ZONE.London,
44+
memory_amount = 1024,
45+
hostname = "my.example.1",
46+
zone = ZONE.London,
4747
storage_devices = [
4848
Storage(os = "Ubuntu 14.04", size=10, tier=maxiops, title='The OS drive'),
4949
Storage(size=10),
@@ -56,7 +56,7 @@ def create_server(self, server):
5656
- size defaults to 10,
5757
- title defaults to hostname + " OS disk" and hostname + " storage disk id" (id is a running starting from 1)
5858
- tier defaults to maxiops
59-
- valid operating systems are:
59+
- valid operating systems are:
6060
"CentOS 6.5", "CentOS 7.0"
6161
"Debian 7.8"
6262
"Ubuntu 12.04", "Ubuntu 14.04"
@@ -76,17 +76,25 @@ def modify_server(self, UUID, **kwargs):
7676
```python
7777
def delete_server(self, UUID):
7878
"""
79-
DELETE '/server/UUID'. Permanently destroys the virtual machine.
79+
DELETE '/server/UUID'. Permanently destroys the virtual machine.
8080
DOES NOT remove the storage disks.
8181
8282
Returns an empty object.
8383
"""
8484
```
8585

86+
```python
87+
def get_server_by_ip(self, ip_address):
88+
"""
89+
Return a (populated) Server instance by its IP.
90+
Uses GET '/ip_address/x.x.x.x' to retrieve machine UUID using IP-address.
91+
"""
92+
```
93+
8694
```python
8795
def get_server_data(self, UUID):
8896
"""
89-
Returns '/server/uuid' data in Python dict.
97+
Returns '/server/uuid' data in Python dict.
9098
Creates object representations of any IP-address and Storage.
9199
"""
92-
```
100+
```

docs/storage-mixin.md

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,68 @@ def detach_storage(self, server_uuid, address):
6262
"""
6363
Detach a Storage object to a Server. Return a list of the server's storages.
6464
"""
65-
```
65+
```
66+
67+
```python
68+
def load_cd_rom(self, server, address):
69+
"""
70+
Loads a storage as a CD-ROM in the CD-ROM device of a server. Returns a list of the server's storages.
71+
"""
72+
```
73+
74+
```python
75+
def eject_cd_rom(self, server):
76+
"""
77+
Ejects the storage from the CD-ROM device of a server. Returns a list of the server's storages.
78+
"""
79+
```
80+
81+
```python
82+
def create_storage_backup(self, storage, title):
83+
"""
84+
Creates a point-in-time backup of a storage resource. Returns a storage object.
85+
"""
86+
```
87+
88+
```python
89+
def restore_storage_backup(self, storage):
90+
"""
91+
Restores the origin storage with data from the specified backup storage. Returns a storage object.
92+
"""
93+
```
94+
95+
```python
96+
def templatize_storage(self, storage, title):
97+
"""
98+
Creates an exact copy of an existing storage resource which can be used as a template for creating new servers. Returns a storage object.
99+
"""
100+
```
101+
102+
```python
103+
def create_storage_import(self, storage, source, source_location=None):
104+
"""
105+
Creates an import task to import data into an existing storage and returns a storage import object.
106+
Source types: http_import or direct_upload.
107+
"""
108+
```
109+
110+
```python
111+
def upload_file_for_storage_import(self, storage_import, file):
112+
"""
113+
Uploads a file directly to UpCloud's uploader session. Returns written bytes, md5sum and sha256sum.
114+
"""
115+
```
116+
117+
```python
118+
def get_storage_import_details(self, storage):
119+
"""
120+
Returns detailed information of an ongoing or finished import task within a storage import object.
121+
"""
122+
```
123+
124+
```python
125+
def cancel_storage_import(self, storage):
126+
"""
127+
Cancels an ongoing import task. Returns a storage import object.
128+
"""
129+
```

test/conftest.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,22 +51,25 @@ def mock_get(target, response_file=None):
5151
return data
5252

5353
@staticmethod
54-
def __put_post_callback(request, target, data):
54+
def __put_post_callback(request, target, data, ignore_data_field=False, empty_payload=False):
5555
data_field = target.split("/")[0]
56-
payload = json.loads(request.body)
5756

58-
for field in data[data_field]:
59-
if field in payload[data_field]:
60-
data[data_field][field] = payload[data_field][field]
57+
if not empty_payload:
58+
payload = json.loads(request.body)
59+
60+
if not ignore_data_field:
61+
for field in data[data_field]:
62+
if field in payload[data_field]:
63+
data[data_field][field] = payload[data_field][field]
6164
return(200, {}, json.dumps(data))
6265

6366
@staticmethod
64-
def mock_post(target, empty_content=False):
67+
def mock_post(target, empty_content=False, ignore_data_field=False, empty_payload=False):
6568

6669
def callback(request):
6770
if not empty_content:
6871
data = json.loads(Mock.read_from_file(target + '_post.json'))
69-
return Mock.__put_post_callback(request, target, data)
72+
return Mock.__put_post_callback(request, target, data, ignore_data_field, empty_payload)
7073
else:
7174
return(200, {}, '{}')
7275

@@ -75,13 +78,14 @@ def callback(request):
7578
content_type='application/json')
7679

7780
@staticmethod
78-
def mock_put(target):
81+
def mock_put(target, ignore_data_field=False, empty_payload=False, call_api=True):
7982
data = json.loads(Mock.read_from_file(target + '.json'))
8083

8184
def callback(request):
82-
return Mock.__put_post_callback(request, target, data)
85+
return Mock.__put_post_callback(request, target, data, ignore_data_field, empty_payload)
8386

84-
responses.add_callback(responses.PUT, Mock.base_url + '/' + target,
87+
url = Mock.base_url + '/' + target if call_api else target
88+
responses.add_callback(responses.PUT, url,
8589
callback=callback,
8690
content_type='application/json')
8791

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"written_bytes":909500125,"md5sum":"5cc6f7e7a1c52303ac3137d62410eec5","sha256sum":"bdf14d897406939c11a73d0720ca75c709e756d437f8be9ee26af6b58ede3bd7"}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"server" : {
3+
"boot_order" : "disk",
4+
"core_number" : "0",
5+
"firewall" : "on",
6+
"hostname" : "fi.example.com",
7+
"ip_addresses" : {
8+
"ip_address" : [
9+
{
10+
"access" : "private",
11+
"address" : "10.0.0.0",
12+
"family": "IPv4"
13+
},
14+
{
15+
"access" : "public",
16+
"address" : "0.0.0.0",
17+
"family": "IPv4"
18+
}
19+
]
20+
},
21+
"license" : 0,
22+
"memory_amount" : "1024",
23+
"nic_model" : "e1000",
24+
"state" : "started",
25+
"storage_devices" : {
26+
"storage_device" : [
27+
{
28+
"address" : "virtio:0",
29+
"storage" : "012580a1-32a1-466e-a323-689ca16f2d43",
30+
"storage_size" : 100,
31+
"storage_title" : "Storage for server1.example.com",
32+
"type" : "disk"
33+
}
34+
]
35+
},
36+
"tags": {
37+
"tag": [
38+
"web1",
39+
"web2"
40+
]
41+
},
42+
"timezone" : "UTC",
43+
"title" : "Helsinki server",
44+
"uuid" : "00798b85-efdc-41ca-8021-f6ef457b8531",
45+
"video_model" : "cirrus",
46+
"vnc" : "on",
47+
"vnc_host" : "fi-he1l.vnc.upcloud.com",
48+
"vnc_password" : "aabbccdd",
49+
"vnc_port" : "00000",
50+
"zone" : "fi-hel1",
51+
"plan": "custom"
52+
}
53+
}

0 commit comments

Comments
 (0)