Skip to content

Commit 0e5d981

Browse files
author
Elias Nygren
committed
Merge pull request #3 from UpCloudLtd/firewall-feature
Firewall feature
2 parents ffc4a99 + d0e5223 commit 0e5d981

File tree

15 files changed

+913
-61
lines changed

15 files changed

+913
-61
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ To test against python3.2=< and pypy3-2.4.0, run:
172172
tox
173173
```
174174

175+
The project also supplies a small test suite to test against the live API at `test/live_test.py`. This suite is NOT run with `py.test` as it will permanently remove all resources related to an account. It should only be run with a throwaway dev-only account when preparing for a new release. It is not shipped with PyPI releases. See source code on how to run the live tests.
176+
175177
## Bugs, Issues, Problems, Ideas
176178

177179
Feel free to open a new issue : )

docs/Firewall.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
The code examples use the following:
2+
3+
```python
4+
import upcloud
5+
from upcloud import FirewallRule
6+
7+
manager = upcloud.CloudManager("username", "password")
8+
```
9+
10+
# About
11+
12+
Firewall is configured with FirewallRule objects that are specific to each server.
13+
Please note that a servers firewall rules are ignored if firewall is turned off
14+
(see [Server](/server) and [API documentation](https://www.upcloud.com/api/7-servers/#modify-server)).
15+
16+
If a server is removed, its firewall and thus its firewall rules are removed too.
17+
18+
Please refer to the [API documentation](https://www.upcloud.com/api/10-firewall/#create-firewall-rule)
19+
for more info on the attributes of FirewallRule.
20+
21+
## List / Get
22+
23+
```python
24+
server = manager.get_servers()[0]
25+
26+
# all firewall rules
27+
firewall_rules = server.get_firewall_rules()
28+
```
29+
30+
## Create
31+
32+
```python
33+
server = manager.get_servers()[0]
34+
35+
rule = server.add_firewall_rule(
36+
FirewallRule(
37+
position = "1",
38+
direction = "in",
39+
family = "IPv4",
40+
protocol = "tcp",
41+
source_address_start = "192.168.1.1",
42+
source_address_end = "192.168.1.255",
43+
destination_port_start = "22",
44+
destination_port_end = "22",
45+
action = "accept"
46+
)
47+
)
48+
```
49+
50+
### Configure Firewall
51+
52+
Server provides a helper function to add several firewall rules in series.
53+
Please note that the function does not know about pre-existing rules
54+
(UpCloud servers are created without any firewall rules by default).
55+
56+
```python
57+
server = manager.get_servers()[0]
58+
59+
rules = server.configure_firewall(
60+
[
61+
FirewallRule(
62+
position = "1",
63+
direction = "in",
64+
family = "IPv4",
65+
protocol = "tcp",
66+
source_address_start = "192.168.1.1",
67+
source_address_end = "192.168.1.255",
68+
destination_port_start = "22",
69+
destination_port_end = "22",
70+
action = "accept"
71+
),
72+
FirewallRule(
73+
position = "2",
74+
direction = "in",
75+
family = "IPv4",
76+
protocol = "tcp",
77+
source_address_start = "192.168.1.1",
78+
source_address_end = "192.168.1.255",
79+
destination_port_start = "21",
80+
destination_port_end = "21",
81+
action = "accept"
82+
)
83+
]
84+
)
85+
```
86+
87+
## Destroy
88+
89+
```python
90+
server = manager.get_servers()[0]
91+
server.get_firewall_rules()[0].destroy()
92+
```
93+
94+
### Destroying all firewall rules
95+
96+
Due to how the API handles positions, the following will NOT work:
97+
98+
```python
99+
# does NOT work
100+
for rule in server.get_firewall_rules():
101+
rule.destroy()
102+
```
103+
104+
This is because rules are based on position and the positions are always so
105+
that they start from 1 and are increment by one for each consecutive rule.
106+
107+
A better approach would be to use CloudManager/FirewallManager directly
108+
(CloudManager and its mixins provide API functionality to Server, Storage, FirewallRule, etc. objects)
109+
110+
```python
111+
for rule in server.get_firewall_rules():
112+
manager.delete_firewall_rule(server.uuid, 1)
113+
```
114+
115+
116+
117+

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pages:
66
- [Server.md, Usage, Server]
77
- [Storage.md, Usage, Storage]
88
- [IP-address.md, Usage, IP-address]
9+
- [Firewall.md, Usage, Firewall]
910
- [CloudManager.md, CloudManager API, General Info]
1011
- [server-mixin.md, CloudManager API, Server Manager]
1112
- [storage-mixin.md, CloudManager API, Storage Manager]

test/conftest.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,23 @@ def manager():
1111

1212

1313
class Mock():
14-
base_url = 'https://api.upcloud.com/1.1'
14+
base_url = 'https://api.upcloud.com/1.2'
1515

1616
@staticmethod
1717
def read_from_file(filename):
18-
18+
1919
filename = filename.replace("/", "_")
2020

2121
cwd = os.path.dirname(__file__)
2222
f = open(cwd + '/json_data/'+filename, 'r')
2323
return f.read()
2424

2525
@staticmethod
26-
def mock_get(target):
27-
data = Mock.read_from_file(target + '.json')
26+
def mock_get(target, response_file=None):
27+
if not response_file:
28+
response_file = target + '.json'
29+
30+
data = Mock.read_from_file(response_file)
2831
responses.add(responses.GET, Mock.base_url + '/' + target,
2932
body=data,
3033
status=200,
@@ -35,11 +38,10 @@ def mock_get(target):
3538
def __put_post_callback(request, target, data):
3639
data_field = target.split("/")[0]
3740
payload = json.loads(request.body)
38-
41+
3942
for field in data[data_field]:
4043
if field in payload[data_field]:
4144
data[data_field][field] = payload[data_field][field]
42-
print(data)
4345
return(200, {}, json.dumps(data))
4446

4547
@staticmethod
@@ -76,7 +78,7 @@ def mock_server_operation(target):
7678
targetfile = "/".join( targetsplit[:2] )
7779

7880
data = json.loads( Mock.read_from_file(targetfile + '.json') )
79-
81+
8082
# API will always respond state: "started", see: Server.stop, Server.start, Server,restart
8183
data["server"]["state"] = "started"
8284

test/json_data/firewall_rule.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"firewall_rule" : {
3+
"protocol" : "tcp",
4+
"destination_port_end" : "22",
5+
"source_address_start" : "192.168.1.0",
6+
"source_address_end" : "192.168.1.255",
7+
"position" : "1",
8+
"source_port_end" : "",
9+
"source_port_start" : "",
10+
"destination_address_start" : "",
11+
"direction" : "in",
12+
"action" : "accept",
13+
"icmp_type" : "",
14+
"destination_port_start" : "22",
15+
"destination_address_end" : ""
16+
}
17+
}

test/json_data/firewall_rules.json

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
{
2+
"firewall_rules": {
3+
"firewall_rule" : [
4+
{
5+
"action" : "accept",
6+
"destination_address_end" : "",
7+
"destination_address_start" : "",
8+
"destination_port_end" : "80",
9+
"destination_port_start" : "80",
10+
"direction" : "in",
11+
"family" : "IPv4",
12+
"icmp_type" : "",
13+
"position" : "1",
14+
"protocol" : "",
15+
"source_address_end" : "",
16+
"source_address_start" : "",
17+
"source_port_end" : "",
18+
"source_port_start" : ""
19+
},
20+
{
21+
"action" : "accept",
22+
"destination_address_end" : "",
23+
"destination_address_start" : "",
24+
"destination_port_end" : "22",
25+
"destination_port_start" : "22",
26+
"direction" : "in",
27+
"family" : "IPv4",
28+
"icmp_type" : "",
29+
"position" : "2",
30+
"protocol" : "tcp",
31+
"source_address_end" : "192.168.1.255",
32+
"source_address_start" : "192.168.1.1",
33+
"source_port_end" : "",
34+
"source_port_start" : ""
35+
},
36+
{
37+
"action" : "accept",
38+
"destination_address_end" : "",
39+
"destination_address_start" : "",
40+
"destination_port_end" : "22",
41+
"destination_port_start" : "22",
42+
"direction" : "in",
43+
"family" : "IPv6",
44+
"icmp_type" : "",
45+
"position" : "3",
46+
"protocol" : "tcp",
47+
"source_address_end" : "2a04:3540:1000:aaaa:bbbb:cccc:d001",
48+
"source_address_start" : "2a04:3540:1000:aaaa:bbbb:cccc:d001",
49+
"source_port_end" : "",
50+
"source_port_start" : ""
51+
},
52+
{
53+
"action" : "accept",
54+
"destination_address_end" : "",
55+
"destination_address_start" : "",
56+
"destination_port_end" : "",
57+
"destination_port_start" : "",
58+
"direction" : "in",
59+
"family" : "IPv4",
60+
"icmp_type" : "8",
61+
"position" : "4",
62+
"protocol" : "icmp",
63+
"source_address_end" : "",
64+
"source_address_start" : "",
65+
"source_port_end" : "",
66+
"source_port_start" : ""
67+
},
68+
{
69+
"action" : "drop",
70+
"destination_address_end" : "",
71+
"destination_address_start" : "",
72+
"destination_port_end" : "",
73+
"destination_port_start" : "",
74+
"direction" : "in",
75+
"family" : "",
76+
"icmp_type" : "",
77+
"position" : "5",
78+
"protocol" : "",
79+
"source_address_end" : "",
80+
"source_address_start" : "",
81+
"source_port_end" : "",
82+
"source_port_start" : ""
83+
}
84+
]
85+
}
86+
}

0 commit comments

Comments
 (0)