Skip to content

Commit fde8913

Browse files
author
Elias Nygren
committed
much leaner way to define servers and storages
1 parent 27f7d7e commit fde8913

File tree

10 files changed

+380
-111
lines changed

10 files changed

+380
-111
lines changed

tests/json_data/server_create.json

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"server" : {
3+
"boot_order" : "disk",
4+
"core_number" : "2",
5+
"firewall" : "off",
6+
"hostname" : "my.example.com",
7+
"ip_addresses" : {
8+
"ip_address" : [
9+
{
10+
"access" : "private",
11+
"address" : "10.3.0.49"
12+
},
13+
{
14+
"access" : "public",
15+
"address" : "38.100.118.31"
16+
}
17+
]
18+
},
19+
"license" : 0,
20+
"memory_amount" : "1024",
21+
"nic_model" : "virtio",
22+
"state" : "started",
23+
"storage_devices" : {
24+
"storage_device" : [
25+
{
26+
"address" : "virtio:0",
27+
"storage" : "01215a5a-c330-4565-81ca-0e0e22eac672",
28+
"storage_size" : 20,
29+
"storage_title" : "Ubuntu 14.04 from template",
30+
"type" : "disk"
31+
},
32+
{
33+
"address" : "virtio:1",
34+
"storage" : "01a8fc6b-8aa3-42ff-bbce-ed5c544bbd74",
35+
"storage_size" : 100,
36+
"storage_title" : "storage disk 1",
37+
"type" : "disk"
38+
}
39+
]
40+
},
41+
"timezone" : "UTC",
42+
"title" : "my.example.com",
43+
"uuid" : "00825599-2df7-4a83-99b4-c9a9c6812f59",
44+
"video_model" : "cirrus",
45+
"vnc" : "off",
46+
"vnc_password" : "aabbccdd",
47+
"zone" : "us-chi1"
48+
}
49+
}

tests/test_server_creation.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
from conftest import Mock
2+
from upcloud import ZONE
3+
from upcloud import Server
4+
from upcloud import Storage
5+
import responses
6+
import json
7+
import pytest
8+
9+
class TestCreateServer():
10+
11+
12+
def test_storage_prepare_post_body(self, manager):
13+
s1 = Storage(os="Ubuntu 14.04", size=10)
14+
body1 = s1.prepare_post_body("my.example.com", 1)
15+
16+
assert body1['title'] == "my.example.com OS disk"
17+
assert body1['tier'] == "maxiops"
18+
assert body1['size'] == 10
19+
assert body1['storage'] == '01000000-0000-4000-8000-000030040200'
20+
assert body1['action'] == 'clone'
21+
22+
s2 = Storage(size=100)
23+
body2 = s2.prepare_post_body("my.example.com", 1)
24+
25+
assert body2['title'] == 'my.example.com storage disk 1'
26+
assert body2['tier'] == 'maxiops'
27+
assert body2['action'] == 'create'
28+
assert body2['size'] == 100
29+
30+
def test_server_init(self, manager):
31+
server1 = Server(core_number=2, memory_amount=1024, hostname="my.example.com",zone=ZONE.Chicago, storage_devices=[
32+
Storage(os="Ubuntu 14.04", size=10),
33+
Storage(size=100, title="storage disk 1")
34+
])
35+
36+
assert server1.title == "my.example.com"
37+
assert server1.core_number == 2
38+
assert server1.memory_amount == 1024
39+
assert server1.hostname == server1.title
40+
assert server1.zone == "us-chi1"
41+
42+
43+
def test_server_prepare_post_body(self):
44+
server = Server(core_number=2, memory_amount=1024, hostname="my.example.com",zone=ZONE.Chicago, storage_devices=[
45+
Storage(os="Ubuntu 14.04", size=10),
46+
Storage()
47+
])
48+
body = server.prepare_post_body()
49+
50+
s1 = body["server"]["storage_devices"]["storage_device"][0]
51+
assert s1['title'] == "my.example.com OS disk"
52+
assert s1['tier'] == "maxiops"
53+
assert s1['size'] == 10
54+
assert s1['storage'] == '01000000-0000-4000-8000-000030040200'
55+
assert s1['action'] == 'clone'
56+
s2 = body["server"]["storage_devices"]["storage_device"][1]
57+
assert s2['title'] == 'my.example.com storage disk 1'
58+
assert s2['tier'] == 'maxiops'
59+
assert s2['action'] == 'create'
60+
assert s2['size'] == 10
61+
62+
assert body["server"]["title"] == "my.example.com"
63+
assert body["server"]["core_number"] == 2
64+
assert body["server"]["memory_amount"] == 1024
65+
assert body["server"]["hostname"] == server.title
66+
assert body["server"]["zone"] == "us-chi1"
67+
68+
@responses.activate
69+
def test_create_server(self, manager):
70+
71+
responses.add(
72+
responses.POST,
73+
Mock.base_url + "/server",
74+
body = Mock.read_from_file("server_create.json"),
75+
status = 202,
76+
content_type='application/json'
77+
)
78+
79+
server1 = Server(core_number=2, memory_amount=1024, hostname="my.example.com",zone=ZONE.Chicago, storage_devices=[
80+
Storage(os="Ubuntu 14.04", size=10),
81+
Storage(size=100, title="storage disk 1")
82+
])
83+
84+
manager.create_server(server1)
85+
86+
# assert correct values in response
87+
assert type(server1).__name__ == "Server"
88+
assert server1.core_number == "2"
89+
assert server1.memory_amount == "1024"
90+
91+
# assert new data was populated
92+
assert server1.video_model == "cirrus"
93+
assert server1.vnc == "off"
94+
assert server1.vnc_password == "aabbccdd"
95+

upcloud/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@
1414
from .ip_address import IP_address
1515
from .firewall import Firewall
1616
from .cloud_manager import CloudManager
17+
from .tools import OperatingSystems
18+
from .tools import ZONE

upcloud/cloud_manager/ip_address_mixin.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
from ..ip_address import IP_address
2-
from ..tools import _create_ip_address_obj
3-
from ..tools import _create_ip_address_objs
42

53

64
class IPManager():
@@ -10,12 +8,12 @@ class IPManager():
108

119
def get_IP(self, UUID):
1210
res = self.get_request("/ip_address/" + UUID)
13-
IP = _create_ip_address_obj( res["ip_address"], cloud_manager=self )
11+
IP = IP_address._create_ip_address_obj( res["ip_address"], cloud_manager=self )
1412
return IP
1513

1614
def get_IPs(self):
1715
res = self.get_request("/ip_address")
18-
IPs = _create_ip_address_objs( res["ip_addresses"], cloud_manager=self )
16+
IPs = IP_address._create_ip_address_objs( res["ip_addresses"], cloud_manager=self )
1917
return IPs
2018

2119
def attach_IP(self, server_UUID):
@@ -25,7 +23,7 @@ def attach_IP(self, server_UUID):
2523
}
2624

2725
res = self.request("POST", "/ip_address", body)
28-
IP = _create_ip_address_obj( res["ip_address"], cloud_manager=self )
26+
IP = IP_address._create_ip_address_obj( res["ip_address"], cloud_manager=self )
2927
return IP
3028

3129
def modify_IP(self, IP_addr, ptr_record):
@@ -35,7 +33,7 @@ def modify_IP(self, IP_addr, ptr_record):
3533
}
3634

3735
res = self.request("PUT", "/ip_address/" + IP_addr, body)
38-
IP = _create_ip_address_obj( res["ip_address"], cloud_manager=self )
36+
IP = IP_address._create_ip_address_obj( res["ip_address"], cloud_manager=self )
3937
return IP
4038

4139
def release_IP(self, IP_addr):

upcloud/cloud_manager/server_mixin.py

Lines changed: 57 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
from ..tools import _create_ip_address_objs
2-
from ..tools import _create_storage_objs
1+
from ..ip_address import IP_address
2+
from ..storage import Storage
33

44
from ..server import Server
55

6+
from ..tools import assignIfExists
7+
8+
69
class ServerManager():
710
"""
811
Functions for managing IP-addresses. Intended to be used as a mixin for CloudManager.
@@ -40,52 +43,58 @@ def get_server(self, UUID):
4043
cloud_manager = self )
4144

4245

43-
def create_server(self, VM, storage_devices):
44-
body = dict()
45-
body["server"] = {
46-
"core_number": VM["cores"],
47-
"memory_amount": VM["ram"],
48-
"hostname": VM["hostname"],
49-
"zone": VM["zone"],
50-
"title": VM["title"],
51-
"storage_devices": {}
52-
}
53-
body["server"]["storage_devices"] = {
54-
"storage_device": []
55-
}
56-
57-
for storage in storage_devices:
58-
device = dict()
59-
device["action"] = storage["action"]
60-
device["tier"] = storage["tier"]
61-
device["title"] = storage["title"]
62-
63-
if device["action"] == "create" or device["action"] == "clone":
64-
device["size"] = storage["size"]
65-
66-
if device["action"] == "attach" or device["action"] == "clone":
67-
device["storage"] = storage["storage"]
68-
69-
if "type" in storage:
70-
device["type"] = storage["type"]
71-
72-
body["server"]["storage_devices"]["storage_device"].append(device)
46+
def create_server(self, server):
47+
"""
48+
Creates a server and its storages based on a (locally created) Server object.
49+
Populates the given Server instance with the API response.
50+
51+
Example:
52+
server1 = Server( core_number = 1,
53+
memory_amount = 512,
54+
hostname = "my.example.1",
55+
zone = ZONE.London,
56+
storage_devices = [
57+
Storage(os = "Ubuntu 14.04", size=10, tier=maxiops, title='The OS drive'),
58+
Storage(size=10),
59+
Storage()
60+
title = "My Example Server"
61+
])
62+
manager.create_server(server1)
63+
64+
All Server fields are mandatory except title (defaults to hostname).
65+
66+
One storage should contain an OS. Otherwise storage fields are optional.
67+
- size defaults to 10,
68+
- title defaults to hostname + " OS disk" and hostname + " storage disk id" (id is a running starting from 1)
69+
- tier defaults to maxiops
70+
- valid operating systems are:
71+
"CentOS 6.5", "CentOS 7.0"
72+
"Debian 7.8"
73+
"Ubuntu 12.04", "Ubuntu 14.04"
74+
"Windows 2003","Windows 2008" ,"Windows 2012"
75+
76+
"""
77+
78+
body = server.prepare_post_body()
7379

7480
res = self.post_request("/server", body)
75-
server = res["server"]
7681

7782
# Populate subobjects
78-
IP_addresses = _create_ip_address_objs( server.pop("ip_addresses"), cloud_manager = self )
79-
storages = _create_storage_objs( server.pop("storage_devices"), cloud_manager = self )
83+
IP_addresses = IP_address._create_ip_address_objs( res["server"].pop("ip_addresses"), cloud_manager = self )
84+
storages = Storage._create_storage_objs( res["server"].pop("storage_devices"), cloud_manager = self )
8085

81-
return Server( server,
82-
ip_addresses = IP_addresses,
83-
storage_devices = storages,
84-
populated = True,
85-
cloud_manager = self )
86+
server._reset( res["server"],
87+
ip_addresses = IP_addresses,
88+
storage_devices = storages,
89+
populated = True)
90+
return server
8691

8792

8893
def modify_server(self, UUID, **kwargs):
94+
"""
95+
modify_server allows updating the server's updateable_fields.
96+
Note: Server's IP-addresses and Storages are managed by their own add/remove methods.
97+
"""
8998
body = dict()
9099
body["server"] = {}
91100
for arg in kwargs:
@@ -97,8 +106,8 @@ def modify_server(self, UUID, **kwargs):
97106
server = res["server"]
98107

99108
# Populate subobjects
100-
IP_addresses = _create_ip_address_objs( server.pop("ip_addresses"), cloud_manager = self )
101-
storages = _create_storage_objs( server.pop("storage_devices"), cloud_manager = self )
109+
IP_addresses = IP_address._create_ip_address_objs( server.pop("ip_addresses"), cloud_manager = self )
110+
storages = Storage._create_storage_objs( server.pop("storage_devices"), cloud_manager = self )
102111

103112
return Server( server,
104113
ip_addresses = IP_addresses,
@@ -108,6 +117,10 @@ def modify_server(self, UUID, **kwargs):
108117

109118

110119
def delete_server(self, UUID):
120+
"""
121+
DELETE '/server/UUID'. Permanently destroys the virtual machine.
122+
DOES NOT remove the storage disks.
123+
"""
111124
return self.request("DELETE", "/server/" + UUID)
112125

113126
def __fetch_servers(self):
@@ -127,7 +140,7 @@ def get_server_data(self, UUID):
127140
server = data["server"]
128141

129142
# Populate subobjects
130-
IP_addresses = _create_ip_address_objs( server.pop("ip_addresses"), cloud_manager = self )
131-
storages = _create_storage_objs( server.pop("storage_devices"), cloud_manager = self )
143+
IP_addresses = IP_address._create_ip_address_objs( server.pop("ip_addresses"), cloud_manager = self )
144+
storages = Storage._create_storage_objs( server.pop("storage_devices"), cloud_manager = self )
132145

133146
return server, IP_addresses, storages

upcloud/cloud_manager/storage_mixin.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
from ..storage import Storage
2-
from ..tools import _create_storage_obj
3-
from ..tools import _create_storage_objs
42

53
class StorageManager():
64
"""
@@ -13,14 +11,14 @@ def get_storages(self, storage_type=""):
1311
Storage types: public, private, normal, backup, cdrom, template, favorite
1412
"""
1513
res = self.get_request("/storage/" + storage_type)
16-
return _create_storage_objs( res["storages"], cloud_manager = self )
14+
return Storage._create_storage_objs( res["storages"], cloud_manager = self )
1715

1816
def get_storage(self, UUID):
1917
"""
2018
Returns a Storage object from the API.
2119
"""
2220
res = self.get_request("/storage/" + UUID)
23-
return _create_storage_obj( res["storage"], cloud_manager = self )
21+
return Storage._create_storage_obj( res["storage"], cloud_manager = self )
2422

2523
def create_storage(self, size, tier, title, zone):
2624
"""
@@ -34,7 +32,7 @@ def create_storage(self, size, tier, title, zone):
3432
"zone": zone
3533
}
3634
res = self.post_request("/storage", body)
37-
return _create_storage_obj( res["storage"], cloud_manager = self )
35+
return Storage._create_storage_obj( res["storage"], cloud_manager = self )
3836

3937
def modify_storage(self, UUID, size, title):
4038
"""
@@ -43,7 +41,7 @@ def modify_storage(self, UUID, size, title):
4341

4442
body = Storage.prepare_put_body(size, title)
4543
res = self.request("PUT","/storage/" + UUID, body)
46-
return _create_storage_obj( res["storage"], cloud_manager = self )
44+
return Storage._create_storage_obj( res["storage"], cloud_manager = self )
4745

4846
def delete_storage(self, UUID):
4947
"""
@@ -62,7 +60,7 @@ def attach_storage(self, server_uuid, storage_uuid, storage_type, address):
6260

6361
res = self.post_request("/server/" + server_uuid + "/storage/attach", body)
6462
print(res)
65-
return _create_storage_objs( res["server"]["storage_devices"], cloud_manager = self )
63+
return Storage._create_storage_objs( res["server"]["storage_devices"], cloud_manager = self )
6664

6765
def detach_storage(self, server_uuid, address):
6866
"""
@@ -71,4 +69,4 @@ def detach_storage(self, server_uuid, address):
7169
body = { "storage_device": { "address": address } }
7270
res = self.post_request("/server/" + server_uuid + "/storage/detach", body)
7371
print(res)
74-
return _create_storage_objs( res["server"]["storage_devices"], cloud_manager = self )
72+
return Storage._create_storage_objs( res["server"]["storage_devices"], cloud_manager = self )

0 commit comments

Comments
 (0)