Skip to content

Commit baaee31

Browse files
committed
feat(api): Labels support for servers & storages, server group support
1 parent 5a894a6 commit baaee31

File tree

7 files changed

+155
-17
lines changed

7 files changed

+155
-17
lines changed

requirements-dev.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ packaging==23.1
2626
# via
2727
# build
2828
# pytest
29-
pip-tools==7.1.0
29+
pip-tools==6.14.0
3030
# via -r requirements-dev.in
3131
pluggy==1.2.0
3232
# via pytest

upcloud_api/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
from upcloud_api.interface import Interface
1818
from upcloud_api.ip_address import IPAddress
1919
from upcloud_api.ip_network import IpNetwork
20+
from upcloud_api.label import Label
2021
from upcloud_api.network import Network
2122
from upcloud_api.object_storage import ObjectStorage
2223
from upcloud_api.router import Router
2324
from upcloud_api.server import Server, login_user_block
25+
from upcloud_api.server_group import ServerGroup, ServerGroupAffinityPolicy
2426
from upcloud_api.storage import Storage
2527
from upcloud_api.storage_import import StorageImport
2628
from upcloud_api.tag import Tag

upcloud_api/cloud_manager/server_mixin.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from upcloud_api.api import API
22
from upcloud_api.ip_address import IPAddress
33
from upcloud_api.server import Server
4+
from upcloud_api.server_group import ServerGroup
45
from upcloud_api.storage import BackupDeletionPolicy, Storage
56

67

@@ -91,9 +92,10 @@ def create_server(self, server: Server) -> Server:
9192
memory_amount = 1024,
9293
hostname = "my.example.1",
9394
zone = "uk-lon1",
95+
labels = [Label('role', 'example')],
9496
storage_devices = [
9597
Storage(os = "01000000-0000-4000-8000-000030200200", size=10, tier=maxiops, title='Example OS disk'),
96-
Storage(size=10),
98+
Storage(size=10, labels=[Label('usage', 'data_disk')]),
9799
Storage()
98100
title = "My Example Server"
99101
])
@@ -191,3 +193,22 @@ def get_server_data(self, uuid: str):
191193
storages = Storage._create_storage_objs(server.pop('storage_devices'), cloud_manager=self)
192194

193195
return server, IPAddresses, storages
196+
197+
def create_server_group(self, server_group: ServerGroup) -> ServerGroup:
198+
"""
199+
Creates a new server group. Allows including servers and defining labels.
200+
"""
201+
if not isinstance(server_group, ServerGroup):
202+
server_group = ServerGroup(server_group)
203+
body = server_group.to_dict()
204+
205+
res = self.api.post_request('/server-group', body)
206+
return ServerGroup(cloud_manager=self, **res['server_group'])
207+
208+
def delete_server_group(self, uuid: str):
209+
"""
210+
DELETE '/server-group/UUID'. Destroys the server group, but not attached servers.
211+
212+
Returns an empty object.
213+
"""
214+
return self.api.delete_request(f'/server-group/{uuid}')

upcloud_api/label.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from upcloud_api.upcloud_resource import UpCloudResource
2+
3+
4+
class Label(UpCloudResource):
5+
"""
6+
Class representation of UpCloud resource label
7+
"""
8+
9+
ATTRIBUTES = {
10+
'key': "",
11+
'value': "",
12+
}
13+
14+
def __init__(self, key="", value="") -> None:
15+
"""
16+
Initialize label.
17+
18+
Set both values for label if given
19+
"""
20+
self.key = key
21+
self.value = value
22+
23+
def __str__(self) -> str:
24+
return f"{self.key}={self.value}"
25+
26+
def to_dict(self):
27+
"""
28+
Return a dict that can be serialised to JSON and sent to UpCloud's API.
29+
"""
30+
body = {
31+
'key': self.key,
32+
'value': self.value,
33+
}
34+
35+
return body

upcloud_api/server.py

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from upcloud_api.firewall import FirewallRule
55
from upcloud_api.ip_address import IPAddress
6+
from upcloud_api.server_group import ServerGroup
67
from upcloud_api.storage import Storage
78
from upcloud_api.utils import try_it_n_times
89

@@ -47,31 +48,34 @@ class Server:
4748
'core_number',
4849
'firewall',
4950
'hostname',
51+
'labels',
5052
'memory_amount',
5153
'nic_model',
54+
'plan',
5255
'title',
5356
'timezone',
5457
'video_model',
5558
'vnc',
5659
'vnc_password',
57-
'plan',
5860
]
5961

6062
optional_fields = [
61-
'plan',
62-
'core_number',
63-
'memory_amount',
63+
'avoid_host',
6464
'boot_order',
65+
'core_number',
6566
'firewall',
67+
'labels',
68+
'login_user',
69+
'memory_amount',
6670
'nic_model',
71+
'password_delivery',
72+
'plan',
73+
'server_group',
6774
'timezone',
75+
'metadata',
76+
'user_data',
6877
'video_model',
6978
'vnc_password',
70-
'password_delivery',
71-
'avoid_host',
72-
'login_user',
73-
'user_data',
74-
'metadata',
7579
]
7680

7781
def __init__(self, server=None, **kwargs) -> None:
@@ -149,11 +153,11 @@ def save(self) -> None:
149153
self.cloud_manager.modify_server(self.uuid, **kwargs)
150154
self._reset(kwargs)
151155

152-
def destroy(self):
156+
def destroy(self, delete_storages=False):
153157
"""
154158
Destroy the server.
155159
"""
156-
self.cloud_manager.delete_server(self.uuid)
160+
self.cloud_manager.delete_server(self.uuid, delete_storages=delete_storages)
157161

158162
def shutdown(self, hard: bool = False, timeout: int = 30) -> None:
159163
"""
@@ -335,6 +339,18 @@ def prepare_post_body(self):
335339
if hasattr(self, optional_field):
336340
body['server'][optional_field] = getattr(self, optional_field)
337341

342+
if hasattr(self, 'labels'):
343+
dict_labels = {'label': []}
344+
for label in self.labels:
345+
dict_labels['label'].append(label.to_dict())
346+
body['server']['labels'] = dict_labels
347+
348+
if hasattr(self, 'metadata') and isinstance(self.metadata, bool):
349+
body['server']['metadata'] = "yes" if self.metadata else "no"
350+
351+
if hasattr(self, 'server_group') and isinstance(self.server_group, ServerGroup):
352+
body['server']['server_group'] = f"{self.server_group.uuid}"
353+
338354
# set password_delivery default as 'none' to prevent API from sending
339355
# emails (with credentials) about each created server
340356
if not hasattr(self, 'password_delivery'):

upcloud_api/server_group.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from enum import Enum
2+
3+
from upcloud_api.upcloud_resource import UpCloudResource
4+
5+
6+
class ServerGroupAffinityPolicy(str, Enum):
7+
"""
8+
Enum representation of affinity policy for a server group
9+
"""
10+
11+
STRICT_ANTI_AFFINITY = 'strict'
12+
ANTI_AFFINITY_PREFERRED = 'yes'
13+
NO_ANTI_AFFINITY = 'no'
14+
15+
16+
class ServerGroup(UpCloudResource):
17+
"""
18+
Class representation of UpCloud server group resource
19+
"""
20+
21+
ATTRIBUTES = {
22+
'anti_affinity': ServerGroupAffinityPolicy.NO_ANTI_AFFINITY,
23+
'labels': None,
24+
'servers': None,
25+
'title': None,
26+
'uuid': None,
27+
}
28+
29+
def __str__(self) -> str:
30+
return self.title
31+
32+
def to_dict(self):
33+
"""
34+
Return a dict that can be serialised to JSON and sent to UpCloud's API.
35+
"""
36+
body = {
37+
'title': self.title,
38+
}
39+
40+
if hasattr(self, 'anti_affinity'):
41+
body['anti_affinity'] = f"{self.anti_affinity}"
42+
43+
if hasattr(self, 'servers'):
44+
servers = []
45+
for server in self.servers:
46+
if isinstance(server, server.Server) and hasattr(server, 'uuid'):
47+
servers.append(server.uuid)
48+
else:
49+
servers.append(server)
50+
51+
if hasattr(self, 'labels'):
52+
dict_labels = {'label': []}
53+
for label in self.labels:
54+
dict_labels['label'].append(label.to_dict())
55+
body['labels'] = dict_labels
56+
57+
return {'server_group': body}

upcloud_api/storage.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,16 @@ class Storage(UpCloudResource):
1919
"""
2020

2121
ATTRIBUTES = {
22-
'uuid': None,
23-
'tier': 'maxiops',
24-
'size': 10,
2522
'access': None,
23+
'address': None,
24+
'labels': None,
2625
'license': None,
2726
'state': None,
27+
'size': 10,
28+
'tier': 'maxiops',
2829
'title': '',
2930
'type': None,
30-
'address': None,
31+
'uuid': None,
3132
'zone': None,
3233
}
3334

@@ -146,6 +147,12 @@ def to_dict(self):
146147
if hasattr(self, 'zone') and self.zone:
147148
body['zone'] = self.zone
148149

150+
if hasattr(self, 'labels'):
151+
dict_labels = []
152+
for label in self.labels:
153+
dict_labels.append(label.to_dict())
154+
body['labels'] = dict_labels
155+
149156
return body
150157

151158
@staticmethod

0 commit comments

Comments
 (0)