1
"""Tests for emulating port management with security groups"""
5
from juju import errors
6
from juju.lib.testing import TestCase
7
from juju.providers.openstack.machine import NovaProviderMachine
8
from juju.providers.openstack.ports import NovaPortManager
9
from juju.providers.openstack.tests import OpenStackTestMixin
12
class ProviderPortMgmtTests(OpenStackTestMixin, TestCase):
13
"""Tests for provider exposed port management methods"""
15
def expect_create_rule(self, group_id, proto, port):
16
self.expect_nova_post("os-security-group-rules",
17
{'security_group_rule': {
18
'parent_group_id': group_id,
23
response={'security_group_rule': {
24
'id': 144, 'parent_group_id': group_id,
27
def expect_existing_rule(self, rule_id, proto, port):
28
self.expect_nova_get("servers/1000/os-security-groups",
29
response={'security_groups': [
30
{'name': "juju-testing-1", 'id': 1, 'rules': [{
40
def test_open_port(self):
41
"""Opening a port adds the rule to the appropriate security group"""
42
self.expect_nova_get("servers/1000/os-security-groups",
43
response={'security_groups': [
44
{'name': "juju-testing-1", 'id': 1},
46
self.expect_create_rule(1, "tcp", 80)
49
log = self.capture_logging("juju.openstack", level=logging.DEBUG)
50
machine = NovaProviderMachine('1000', "server1000.testing.invalid")
51
deferred = self.get_provider().open_port(machine, "1", 80)
53
self.assertIn("Opened 80/tcp on machine '1000'",
55
return deferred.addCallback(_check_log)
57
def test_open_port_missing_group(self):
58
"""Missing security group raises an error on deleting port"""
59
self.expect_nova_get("servers/1000/os-security-groups",
60
response={'security_groups': []})
63
machine = NovaProviderMachine('1000', "server1000.testing.invalid")
64
deferred = self.get_provider().open_port(machine, "1", 80)
65
return self.assertFailure(deferred, errors.ProviderInteractionError)
67
def test_close_port(self):
68
"""Closing a port removes the matching rule from the security group"""
69
self.expect_existing_rule(12, "tcp", 80)
70
self.expect_nova_delete("os-security-group-rules/12")
73
log = self.capture_logging("juju.openstack", level=logging.DEBUG)
74
machine = NovaProviderMachine('1000', "server1000.testing.invalid")
75
deferred = self.get_provider().close_port(machine, "1", 80)
77
self.assertIn("Closed 80/tcp on machine '1000'",
79
return deferred.addCallback(_check_log)
81
def test_close_port_missing_group(self):
82
"""Missing security group raises an error on closing port"""
83
self.expect_nova_get("servers/1000/os-security-groups",
84
response={'security_groups': []})
87
machine = NovaProviderMachine('1000', "server1000.testing.invalid")
88
deferred = self.get_provider().close_port(machine, "1", 80)
89
return self.assertFailure(deferred, errors.ProviderInteractionError)
91
def test_close_port_missing_rule(self):
92
"""Missing security group rule raises an error on closing port"""
93
self.expect_nova_get("servers/1000/os-security-groups",
94
response={'security_groups': [{
95
'name': "juju-testing-1", 'id': 1, "rules": [],
99
machine = NovaProviderMachine('1000', "server1000.testing.invalid")
100
deferred = self.get_provider().close_port(machine, "1", 80)
101
return self.assertFailure(deferred, errors.ProviderInteractionError)
103
def test_close_port_mismatching_rule(self):
104
"""Rule with different port raises an error on closing port"""
105
self.expect_existing_rule(12, "tcp", 8080)
108
machine = NovaProviderMachine('1000', "server1000.testing.invalid")
109
deferred = self.get_provider().close_port(machine, "1", 80)
110
return self.assertFailure(deferred, errors.ProviderInteractionError)
112
def test_get_opened_ports_none(self):
113
"""No opened ports are listed when there are no rules"""
114
self.expect_nova_get("servers/1000/os-security-groups",
115
response={'security_groups': [{
116
'name': "juju-testing-1", 'id': 1, "rules": [],
120
machine = NovaProviderMachine('1000', "server1000.testing.invalid")
121
deferred = self.get_provider().get_opened_ports(machine, "1")
122
return deferred.addCallback(self.assertEqual, set())
124
def test_get_opened_ports_one(self):
125
"""Opened port is listed when there is a matching rule"""
126
self.expect_existing_rule(12, "tcp", 80)
129
machine = NovaProviderMachine('1000', "server1000.testing.invalid")
130
deferred = self.get_provider().get_opened_ports(machine, "1")
131
return deferred.addCallback(self.assertEqual, set([(80, "tcp")]))
133
def test_get_opened_ports_group_ignored(self):
134
"""Opened ports exclude rules delegating to other security groups"""
135
self.expect_nova_get("servers/1000/os-security-groups",
136
response={'security_groups': [{
137
'name': "juju-testing-1", 'id': 1, "rules": [{
139
'parent_group_id': 1,
143
'group': {'name': "juju-testing"},
148
machine = NovaProviderMachine('1000', "server1000.testing.invalid")
149
deferred = self.get_provider().get_opened_ports(machine, "1")
150
return deferred.addCallback(self.assertEqual, set())
152
def test_get_opened_ports_multiport_ignored(self):
153
"""Opened ports exclude rules spanning multiple ports"""
154
self.expect_nova_get("servers/1000/os-security-groups",
155
response={'security_groups': [{
156
'name': "juju-testing-1", 'id': 1, "rules": [{
158
'parent_group_id': 1,
159
'ip_protocol': "tcp",
166
machine = NovaProviderMachine('1000', "server1000.testing.invalid")
167
deferred = self.get_provider().get_opened_ports(machine, "1")
168
return deferred.addCallback(self.assertEqual, set())
171
class PortManagerTestMixin(OpenStackTestMixin):
173
def get_port_manager(self):
174
provider = self.get_provider()
175
return NovaPortManager(provider.nova, provider.environment_name)
178
class EnsureGroupsTests(PortManagerTestMixin, TestCase):
179
"""Tests for ensure_groups method used when launching machines"""
181
def expect_create_juju_group(self):
182
self.expect_nova_post("os-security-groups",
184
'name': 'juju-testing',
185
'description': 'juju group for testing',
187
response={'security_group': {
190
self.expect_nova_post("os-security-group-rules",
191
{'security_group_rule': {
192
'parent_group_id': 1,
193
'ip_protocol': "tcp",
197
response={'security_group_rule': {
198
'id': 144, 'parent_group_id': 1,
200
self.expect_nova_post("os-security-group-rules",
201
{'security_group_rule': {
202
'parent_group_id': 1,
204
'ip_protocol': "tcp",
208
response={'security_group_rule': {
209
'id': 145, 'parent_group_id': 1,
212
def expect_create_machine_group(self, machine_id):
213
machine = str(machine_id)
214
self.expect_nova_post("os-security-groups",
216
'name': 'juju-testing-' + machine,
217
'description': 'juju group for testing machine ' + machine,
219
response={'security_group': {
223
def check_group_names(self, result, machine_id):
224
self.assertEqual(["juju-testing", "juju-testing-" + str(machine_id)],
227
def test_none_existing(self):
228
"""When no groups exist juju and machine security groups are created"""
229
self.expect_nova_get("os-security-groups",
230
response={'security_groups': []})
231
self.expect_create_juju_group()
232
self.expect_create_machine_group(0)
234
deferred = self.get_port_manager().ensure_groups(0)
235
return deferred.addCallback(self.check_group_names, 0)
237
def test_other_existing(self):
238
"""Existing groups in a different environment are not affected"""
239
self.expect_nova_get("os-security-groups",
240
response={'security_groups': [
241
{'name': "juju-testingish", 'id': 7},
242
{'name': "juju-testingish-0", 'id': 8},
244
self.expect_create_juju_group()
245
self.expect_create_machine_group(0)
247
deferred = self.get_port_manager().ensure_groups(0)
248
return deferred.addCallback(self.check_group_names, 0)
250
def test_existing_juju_group(self):
251
"""An exisiting juju security group is reused"""
252
self.expect_nova_get("os-security-groups",
253
response={'security_groups': [
254
{'name': "juju-testing", 'id': 1},
256
self.expect_create_machine_group(0)
258
deferred = self.get_port_manager().ensure_groups(0)
259
return deferred.addCallback(self.check_group_names, 0)
261
def test_existing_machine_group(self):
262
"""An existing machine security group is deleted and remade"""
263
self.expect_nova_get("os-security-groups",
264
response={'security_groups': [
265
{'name': "juju-testing-6", 'id': 3},
267
self.expect_create_juju_group()
268
self.expect_nova_delete("os-security-groups/3")
269
self.expect_create_machine_group(6)
271
deferred = self.get_port_manager().ensure_groups(6)
272
return deferred.addCallback(self.check_group_names, 6)
275
class GetMachineGroupsTests(PortManagerTestMixin, TestCase):
276
"""Tests for get_machine_groups method needed for machine shutdown"""
278
def test_normal(self):
279
"""A standard juju machine returns the machine group name and id"""
280
self.expect_nova_get("servers/1000/os-security-groups",
281
response={'security_groups': [
282
{'id': 7, 'name': "juju-testing"},
283
{'id': 8, 'name': "juju-testing-0"},
286
machine = NovaProviderMachine(1000)
287
deferred = self.get_port_manager().get_machine_groups(machine)
288
return deferred.addCallback(self.assertEqual, {"juju-testing-0": 8})
290
def test_normal_include_juju(self):
291
"""If param with_juju_group=True the juju group is also returned"""
292
self.expect_nova_get("servers/1000/os-security-groups",
293
response={'security_groups': [
294
{'id': 7, 'name': "juju-testing"},
295
{'id': 8, 'name': "juju-testing-0"},
298
machine = NovaProviderMachine(1000)
299
deferred = self.get_port_manager().get_machine_groups(machine, True)
300
return deferred.addCallback(self.assertEqual,
301
{"juju-testing": 7, "juju-testing-0": 8})
303
def test_extra_group(self):
304
"""Additional groups not in the juju namespace are ignored"""
305
self.expect_nova_get("servers/1000/os-security-groups",
306
response={'security_groups': [
307
{'id': 1, 'name': "default"},
308
{'id': 7, 'name': "juju-testing"},
309
{'id': 8, 'name': "juju-testing-0"},
312
machine = NovaProviderMachine(1000)
313
deferred = self.get_port_manager().get_machine_groups(machine)
314
return deferred.addCallback(self.assertEqual, {"juju-testing-0": 8})
316
def test_other_group(self):
317
"""A server not managed by juju returns nothing"""
318
self.expect_nova_get("servers/1000/os-security-groups",
319
response={'security_groups': [
320
{'id': 1, 'name': "default"},
323
machine = NovaProviderMachine(1000)
324
deferred = self.get_port_manager().get_machine_groups(machine)
325
return deferred.addCallback(self.assertEqual, None)
327
def test_missing_groups(self):
328
"""A server with no groups returns nothing"""
329
self.expect_nova_get("servers/1000/os-security-groups",
330
response={'security_groups': []})
332
machine = NovaProviderMachine(1000)
333
deferred = self.get_port_manager().get_machine_groups(machine)
334
return deferred.addCallback(self.assertEqual, None)
336
def test_error_missing_server(self):
337
"""A server that doesn't exist or has been deleted returns nothing"""
338
self.expect_nova_get("servers/1000/os-security-groups",
339
code=404, response={"itemNotFound": {
340
"message": "Instance 1000 could not be found.",
344
machine = NovaProviderMachine(1000)
345
deferred = self.get_port_manager().get_machine_groups(machine)
346
return deferred.addCallback(self.assertEqual, None)
347
# XXX: Broken by workaround for HP not supporting this api
348
test_error_missing_server.skip = True
350
def test_error_missing_page(self):
351
"""Unexpected errors from the client are propogated"""
352
self.expect_nova_get("servers/1000/os-security-groups",
353
code=404, response="404 Not Found\n\n"
354
"The resource could not be found.\n\n ")
356
machine = NovaProviderMachine(1000)
357
deferred = self.get_port_manager().get_machine_groups(machine)
358
return self.assertFailure(deferred, errors.ProviderInteractionError)
359
# XXX: Need implemention of fancy error to exception mapping
360
test_error_missing_page.skip = True
362
def test_error_missing_server_fault(self):
363
"A bogus compute fault due to lp:1010486 returns nothing"""
364
self.expect_nova_get("servers/1000/os-security-groups",
365
code=500, response={"computeFault": {
366
"message": "The server has either erred or is incapable of"
367
" performing the requested operation.",
370
self.expect_nova_get("servers/1000",
371
code=404, response={"itemNotFound": {
372
"message": "The resource could not be found.",
376
machine = NovaProviderMachine(1000)
377
deferred = self.get_port_manager().get_machine_groups(machine)
378
return deferred.addCallback(self.assertEqual, None)
379
# XXX: Need implemention of fancy error to exception mapping
380
test_error_missing_server_fault.skip = True
382
def test_error_really_fault(self):
383
"""A real compute fault is propogated"""
384
self.expect_nova_get("servers/1000/os-security-groups",
385
code=500, response={"computeFault": {
386
"message": "The server has either erred or is incapable of"
387
" performing the requested operation.",
390
self.expect_nova_get("servers/1000", response={"server": {"id": 1000}})
392
machine = NovaProviderMachine(1000)
393
deferred = self.get_port_manager().get_machine_groups(machine)
394
return self.assertFailure(deferred, errors.ProviderInteractionError)
395
# XXX: Need implemention of fancy error to exception mapping
396
test_error_really_fault.skip = True