Merge "Show security groups in Port detail page"
This commit is contained in:
commit
4a882c14b9
@ -289,13 +289,13 @@ class SecurityGroupManager(object):
|
|||||||
return [SecurityGroup(sg) for sg in secgroups.get('security_groups')]
|
return [SecurityGroup(sg) for sg in secgroups.get('security_groups')]
|
||||||
|
|
||||||
@profiler.trace
|
@profiler.trace
|
||||||
def list(self):
|
def list(self, **params):
|
||||||
"""Fetches a list all security groups.
|
"""Fetches a list all security groups.
|
||||||
|
|
||||||
:returns: List of SecurityGroup objects
|
:returns: List of SecurityGroup objects
|
||||||
"""
|
"""
|
||||||
tenant_id = self.request.user.tenant_id
|
tenant_id = params.pop('tenant_id', self.request.user.tenant_id)
|
||||||
return self._list(tenant_id=tenant_id)
|
return self._list(tenant_id=tenant_id, **params)
|
||||||
|
|
||||||
def _sg_name_dict(self, sg_id, rules):
|
def _sg_name_dict(self, sg_id, rules):
|
||||||
"""Create a mapping dict from secgroup id to its name."""
|
"""Create a mapping dict from secgroup id to its name."""
|
||||||
@ -1316,8 +1316,8 @@ def floating_ip_supported(request):
|
|||||||
|
|
||||||
|
|
||||||
@memoized
|
@memoized
|
||||||
def security_group_list(request):
|
def security_group_list(request, **params):
|
||||||
return SecurityGroupManager(request).list()
|
return SecurityGroupManager(request).list(**params)
|
||||||
|
|
||||||
|
|
||||||
def security_group_get(request, sg_id):
|
def security_group_get(request, sg_id):
|
||||||
|
@ -31,23 +31,27 @@ NETWORKS_DETAIL_URL = 'horizon:project:networks:detail'
|
|||||||
|
|
||||||
class NetworkPortTests(test.TestCase):
|
class NetworkPortTests(test.TestCase):
|
||||||
|
|
||||||
@test.create_stubs({api.neutron: ('network_get',
|
|
||||||
'port_get',
|
|
||||||
'is_extension_supported',)})
|
|
||||||
def test_port_detail(self):
|
def test_port_detail(self):
|
||||||
self._test_port_detail()
|
self._test_port_detail()
|
||||||
|
|
||||||
@test.create_stubs({api.neutron: ('network_get',
|
|
||||||
'port_get',
|
|
||||||
'is_extension_supported',)})
|
|
||||||
def test_port_detail_with_mac_learning(self):
|
def test_port_detail_with_mac_learning(self):
|
||||||
self._test_port_detail(mac_learning=True)
|
self._test_port_detail(mac_learning=True)
|
||||||
|
|
||||||
|
@test.create_stubs({api.neutron: ('network_get',
|
||||||
|
'port_get',
|
||||||
|
'is_extension_supported',
|
||||||
|
'security_group_list',)})
|
||||||
def _test_port_detail(self, mac_learning=False):
|
def _test_port_detail(self, mac_learning=False):
|
||||||
port = self.ports.first()
|
# Use a port associated with security group
|
||||||
|
port = [p for p in self.ports.list() if p.security_groups][0]
|
||||||
|
sgs = [sg for sg in self.security_groups.list()
|
||||||
|
if sg.id in port.security_groups]
|
||||||
network_id = self.networks.first().id
|
network_id = self.networks.first().id
|
||||||
api.neutron.port_get(IsA(http.HttpRequest), port.id)\
|
api.neutron.port_get(IsA(http.HttpRequest), port.id)\
|
||||||
.AndReturn(self.ports.first())
|
.AndReturn(port)
|
||||||
|
api.neutron.security_group_list(IsA(http.HttpRequest),
|
||||||
|
ids=port.security_groups)\
|
||||||
|
.AndReturn(sgs)
|
||||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||||
'mac-learning')\
|
'mac-learning')\
|
||||||
.MultipleTimes().AndReturn(mac_learning)
|
.MultipleTimes().AndReturn(mac_learning)
|
||||||
|
@ -114,6 +114,20 @@ class DetailView(tabs.TabbedTableView):
|
|||||||
|
|
||||||
return network
|
return network
|
||||||
|
|
||||||
|
@memoized.memoized_method
|
||||||
|
def get_security_groups(self, sg_ids):
|
||||||
|
# Avoid extra API calls if no security group is associated.
|
||||||
|
if not sg_ids:
|
||||||
|
return []
|
||||||
|
try:
|
||||||
|
security_groups = api.neutron.security_group_list(self.request,
|
||||||
|
id=sg_ids)
|
||||||
|
except Exception:
|
||||||
|
security_groups = []
|
||||||
|
msg = _("Unable to retrieve security groups for the port.")
|
||||||
|
exceptions.handle(self.request, msg)
|
||||||
|
return security_groups
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(DetailView, self).get_context_data(**kwargs)
|
context = super(DetailView, self).get_context_data(**kwargs)
|
||||||
port = self.get_data()
|
port = self.get_data()
|
||||||
@ -124,6 +138,7 @@ class DetailView(tabs.TabbedTableView):
|
|||||||
port.network_url = reverse(network_url, args=[port.network_id])
|
port.network_url = reverse(network_url, args=[port.network_id])
|
||||||
for ip in port.fixed_ips:
|
for ip in port.fixed_ips:
|
||||||
ip['subnet_url'] = reverse(subnet_url, args=[ip['subnet_id']])
|
ip['subnet_url'] = reverse(subnet_url, args=[ip['subnet_id']])
|
||||||
|
port.security_groups = self.get_security_groups(port.security_groups)
|
||||||
table = project_tables.PortsTable(self.request,
|
table = project_tables.PortsTable(self.request,
|
||||||
network_id=port.network_id)
|
network_id=port.network_id)
|
||||||
# TODO(robcresswell) Add URL for "Ports" crumb after bug/1416838
|
# TODO(robcresswell) Add URL for "Ports" crumb after bug/1416838
|
||||||
|
@ -83,6 +83,23 @@
|
|||||||
</dl>
|
</dl>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<h4>{% trans "Security Groups" %}</h4>
|
||||||
|
<hr class="header_rule">
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
{% for group in port.security_groups %}
|
||||||
|
<dt>{{ group.name }}</dt>
|
||||||
|
<dd>
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
{% for rule in group.rules %}
|
||||||
|
<li>{{ rule }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</dd>
|
||||||
|
{% empty %}
|
||||||
|
<dd>{% trans "No security group is associated" %}</dd>
|
||||||
|
{% endfor %}
|
||||||
|
</dl>
|
||||||
|
|
||||||
<h4>{% trans "Binding" %}</h4>
|
<h4>{% trans "Binding" %}</h4>
|
||||||
<hr class="header_rule">
|
<hr class="header_rule">
|
||||||
<dl class="dl-horizontal">
|
<dl class="dl-horizontal">
|
||||||
|
@ -776,19 +776,30 @@ class NeutronApiSecurityGroupTests(NeutronApiTestBase):
|
|||||||
for (exprule, retrule) in six.moves.zip(exp_rules, ret_sg.rules):
|
for (exprule, retrule) in six.moves.zip(exp_rules, ret_sg.rules):
|
||||||
self._cmp_sg_rule(exprule, retrule)
|
self._cmp_sg_rule(exprule, retrule)
|
||||||
|
|
||||||
def test_security_group_list(self):
|
def _test_security_group_list(self, **params):
|
||||||
sgs = self.api_security_groups.list()
|
sgs = self.api_security_groups.list()
|
||||||
tenant_id = self.request.user.tenant_id
|
q_params = {'tenant_id': self.request.user.tenant_id}
|
||||||
|
# if tenant_id is specified, the passed tenant_id should be sent.
|
||||||
|
q_params.update(params)
|
||||||
# use deepcopy to ensure self.api_security_groups is not modified.
|
# use deepcopy to ensure self.api_security_groups is not modified.
|
||||||
self.qclient.list_security_groups(tenant_id=tenant_id) \
|
self.qclient.list_security_groups(**q_params) \
|
||||||
.AndReturn({'security_groups': copy.deepcopy(sgs)})
|
.AndReturn({'security_groups': copy.deepcopy(sgs)})
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
rets = api.neutron.security_group_list(self.request)
|
rets = api.neutron.security_group_list(self.request, **params)
|
||||||
self.assertEqual(len(sgs), len(rets))
|
self.assertEqual(len(sgs), len(rets))
|
||||||
for (exp, ret) in six.moves.zip(sgs, rets):
|
for (exp, ret) in six.moves.zip(sgs, rets):
|
||||||
self._cmp_sg(exp, ret)
|
self._cmp_sg(exp, ret)
|
||||||
|
|
||||||
|
def test_security_group_list(self):
|
||||||
|
self._test_security_group_list()
|
||||||
|
|
||||||
|
def test_security_group_list_with_params(self):
|
||||||
|
self._test_security_group_list(name='sg1')
|
||||||
|
|
||||||
|
def test_security_group_list_with_tenant_id(self):
|
||||||
|
self._test_security_group_list(tenant_id='tenant1', name='sg1')
|
||||||
|
|
||||||
def test_security_group_get(self):
|
def test_security_group_get(self):
|
||||||
secgroup = self.api_security_groups.first()
|
secgroup = self.api_security_groups.first()
|
||||||
sg_ids = set([secgroup['id']] +
|
sg_ids = set([secgroup['id']] +
|
||||||
|
@ -114,56 +114,71 @@ def data(TEST):
|
|||||||
TEST.subnets.add(subnet)
|
TEST.subnets.add(subnet)
|
||||||
|
|
||||||
# Ports on 1st network.
|
# Ports on 1st network.
|
||||||
port_dict = {'admin_state_up': True,
|
port_dict = {
|
||||||
'device_id': 'af75c8e5-a1cc-4567-8d04-44fcd6922890',
|
'admin_state_up': True,
|
||||||
'device_owner': 'network:dhcp',
|
'device_id': 'af75c8e5-a1cc-4567-8d04-44fcd6922890',
|
||||||
'fixed_ips': [{'ip_address': '10.0.0.3',
|
'device_owner': 'network:dhcp',
|
||||||
'subnet_id': subnet_dict['id']}],
|
'fixed_ips': [{'ip_address': '10.0.0.3',
|
||||||
'id': '063cf7f3-ded1-4297-bc4c-31eae876cc91',
|
'subnet_id': subnet_dict['id']}],
|
||||||
'mac_address': 'fa:16:3e:9c:d5:7e',
|
'id': '063cf7f3-ded1-4297-bc4c-31eae876cc91',
|
||||||
'name': '',
|
'mac_address': 'fa:16:3e:9c:d5:7e',
|
||||||
'network_id': network_dict['id'],
|
'name': '',
|
||||||
'status': 'ACTIVE',
|
'network_id': network_dict['id'],
|
||||||
'tenant_id': network_dict['tenant_id'],
|
'status': 'ACTIVE',
|
||||||
'binding:vnic_type': 'normal',
|
'tenant_id': network_dict['tenant_id'],
|
||||||
'binding:host_id': 'host',
|
'binding:vnic_type': 'normal',
|
||||||
'allowed_address_pairs': [{'ip_address': '174.0.0.201',
|
'binding:host_id': 'host',
|
||||||
'mac_address': 'fa:16:3e:7a:7b:18'}]
|
'allowed_address_pairs': [
|
||||||
}
|
{'ip_address': '174.0.0.201',
|
||||||
|
'mac_address': 'fa:16:3e:7a:7b:18'}
|
||||||
|
],
|
||||||
|
'security_groups': [],
|
||||||
|
}
|
||||||
|
|
||||||
TEST.api_ports.add(port_dict)
|
TEST.api_ports.add(port_dict)
|
||||||
TEST.ports.add(neutron.Port(port_dict))
|
TEST.ports.add(neutron.Port(port_dict))
|
||||||
|
|
||||||
port_dict = {'admin_state_up': True,
|
port_dict = {
|
||||||
'device_id': '1',
|
'admin_state_up': True,
|
||||||
'device_owner': 'compute:nova',
|
'device_id': '1',
|
||||||
'fixed_ips': [{'ip_address': '10.0.0.4',
|
'device_owner': 'compute:nova',
|
||||||
'subnet_id': subnet_dict['id']}],
|
'fixed_ips': [{'ip_address': '10.0.0.4',
|
||||||
'id': '7e6ce62c-7ea2-44f8-b6b4-769af90a8406',
|
'subnet_id': subnet_dict['id']}],
|
||||||
'mac_address': 'fa:16:3e:9d:e6:2f',
|
'id': '7e6ce62c-7ea2-44f8-b6b4-769af90a8406',
|
||||||
'name': '',
|
'mac_address': 'fa:16:3e:9d:e6:2f',
|
||||||
'network_id': network_dict['id'],
|
'name': '',
|
||||||
'status': 'ACTIVE',
|
'network_id': network_dict['id'],
|
||||||
'tenant_id': network_dict['tenant_id'],
|
'status': 'ACTIVE',
|
||||||
'binding:vnic_type': 'normal',
|
'tenant_id': network_dict['tenant_id'],
|
||||||
'binding:host_id': 'host'}
|
'binding:vnic_type': 'normal',
|
||||||
|
'binding:host_id': 'host',
|
||||||
|
'security_groups': [
|
||||||
|
# sec_group_1 ID below
|
||||||
|
'faad7c80-3b62-4440-967c-13808c37131d',
|
||||||
|
# sec_group_2 ID below
|
||||||
|
'27a5c9a1-bdbb-48ac-833a-2e4b5f54b31d'
|
||||||
|
],
|
||||||
|
}
|
||||||
TEST.api_ports.add(port_dict)
|
TEST.api_ports.add(port_dict)
|
||||||
TEST.ports.add(neutron.Port(port_dict))
|
TEST.ports.add(neutron.Port(port_dict))
|
||||||
assoc_port = port_dict
|
assoc_port = port_dict
|
||||||
|
|
||||||
port_dict = {'admin_state_up': True,
|
port_dict = {
|
||||||
'device_id': '279989f7-54bb-41d9-ba42-0d61f12fda61',
|
'admin_state_up': True,
|
||||||
'device_owner': 'network:router_interface',
|
'device_id': '279989f7-54bb-41d9-ba42-0d61f12fda61',
|
||||||
'fixed_ips': [{'ip_address': '10.0.0.1',
|
'device_owner': 'network:router_interface',
|
||||||
'subnet_id': subnet_dict['id']}],
|
'fixed_ips': [{'ip_address': '10.0.0.1',
|
||||||
'id': '9036eedb-e7fa-458e-bc6e-d9d06d9d1bc4',
|
'subnet_id': subnet_dict['id']}],
|
||||||
'mac_address': 'fa:16:3e:9c:d5:7f',
|
'id': '9036eedb-e7fa-458e-bc6e-d9d06d9d1bc4',
|
||||||
'name': '',
|
'mac_address': 'fa:16:3e:9c:d5:7f',
|
||||||
'network_id': network_dict['id'],
|
'name': '',
|
||||||
'status': 'ACTIVE',
|
'network_id': network_dict['id'],
|
||||||
'tenant_id': network_dict['tenant_id'],
|
'status': 'ACTIVE',
|
||||||
'binding:vnic_type': 'normal',
|
'tenant_id': network_dict['tenant_id'],
|
||||||
'binding:host_id': 'host'}
|
'binding:vnic_type': 'normal',
|
||||||
|
'binding:host_id': 'host',
|
||||||
|
'security_groups': [],
|
||||||
|
}
|
||||||
TEST.api_ports.add(port_dict)
|
TEST.api_ports.add(port_dict)
|
||||||
TEST.ports.add(neutron.Port(port_dict))
|
TEST.ports.add(neutron.Port(port_dict))
|
||||||
|
|
||||||
@ -201,19 +216,25 @@ def data(TEST):
|
|||||||
TEST.networks.add(neutron.Network(network))
|
TEST.networks.add(neutron.Network(network))
|
||||||
TEST.subnets.add(subnet)
|
TEST.subnets.add(subnet)
|
||||||
|
|
||||||
port_dict = {'admin_state_up': True,
|
port_dict = {
|
||||||
'device_id': '2',
|
'admin_state_up': True,
|
||||||
'device_owner': 'compute:nova',
|
'device_id': '2',
|
||||||
'fixed_ips': [{'ip_address': '172.16.88.3',
|
'device_owner': 'compute:nova',
|
||||||
'subnet_id': subnet_dict['id']}],
|
'fixed_ips': [{'ip_address': '172.16.88.3',
|
||||||
'id': '1db2cc37-3553-43fa-b7e2-3fc4eb4f9905',
|
'subnet_id': subnet_dict['id']}],
|
||||||
'mac_address': 'fa:16:3e:56:e6:2f',
|
'id': '1db2cc37-3553-43fa-b7e2-3fc4eb4f9905',
|
||||||
'name': '',
|
'mac_address': 'fa:16:3e:56:e6:2f',
|
||||||
'network_id': network_dict['id'],
|
'name': '',
|
||||||
'status': 'ACTIVE',
|
'network_id': network_dict['id'],
|
||||||
'tenant_id': network_dict['tenant_id'],
|
'status': 'ACTIVE',
|
||||||
'binding:vnic_type': 'normal',
|
'tenant_id': network_dict['tenant_id'],
|
||||||
'binding:host_id': 'host'}
|
'binding:vnic_type': 'normal',
|
||||||
|
'binding:host_id': 'host',
|
||||||
|
'security_groups': [
|
||||||
|
# sec_group_1 ID below
|
||||||
|
'faad7c80-3b62-4440-967c-13808c37131d',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
TEST.api_ports.add(port_dict)
|
TEST.api_ports.add(port_dict)
|
||||||
TEST.ports.add(neutron.Port(port_dict))
|
TEST.ports.add(neutron.Port(port_dict))
|
||||||
@ -315,19 +336,22 @@ def data(TEST):
|
|||||||
TEST.subnets.add(subnet)
|
TEST.subnets.add(subnet)
|
||||||
|
|
||||||
# Set up router data.
|
# Set up router data.
|
||||||
port_dict = {'admin_state_up': True,
|
port_dict = {
|
||||||
'device_id': '7180cede-bcd8-4334-b19f-f7ef2f331f53',
|
'admin_state_up': True,
|
||||||
'device_owner': 'network:router_gateway',
|
'device_id': '7180cede-bcd8-4334-b19f-f7ef2f331f53',
|
||||||
'fixed_ips': [{'ip_address': '10.0.0.3',
|
'device_owner': 'network:router_gateway',
|
||||||
'subnet_id': subnet_dict['id']}],
|
'fixed_ips': [{'ip_address': '10.0.0.3',
|
||||||
'id': '44ec6726-4bdc-48c5-94d4-df8d1fbf613b',
|
'subnet_id': subnet_dict['id']}],
|
||||||
'mac_address': 'fa:16:3e:9c:d5:7e',
|
'id': '44ec6726-4bdc-48c5-94d4-df8d1fbf613b',
|
||||||
'name': '',
|
'mac_address': 'fa:16:3e:9c:d5:7e',
|
||||||
'network_id': TEST.networks.get(name="ext_net")['id'],
|
'name': '',
|
||||||
'status': 'ACTIVE',
|
'network_id': TEST.networks.get(name="ext_net")['id'],
|
||||||
'tenant_id': '1',
|
'status': 'ACTIVE',
|
||||||
'binding:vnic_type': 'normal',
|
'tenant_id': '1',
|
||||||
'binding:host_id': 'host'}
|
'binding:vnic_type': 'normal',
|
||||||
|
'binding:host_id': 'host',
|
||||||
|
'security_groups': [],
|
||||||
|
}
|
||||||
TEST.api_ports.add(port_dict)
|
TEST.api_ports.add(port_dict)
|
||||||
TEST.ports.add(neutron.Port(port_dict))
|
TEST.ports.add(neutron.Port(port_dict))
|
||||||
|
|
||||||
@ -923,20 +947,23 @@ def data(TEST):
|
|||||||
TEST.firewalls.add(fw2)
|
TEST.firewalls.add(fw2)
|
||||||
|
|
||||||
# ports on 4th network
|
# ports on 4th network
|
||||||
port_dict = {'admin_state_up': True,
|
port_dict = {
|
||||||
'device_id': '9872faaa-b2b2-eeee-9911-21332eedaa77',
|
'admin_state_up': True,
|
||||||
'device_owner': 'network:dhcp',
|
'device_id': '9872faaa-b2b2-eeee-9911-21332eedaa77',
|
||||||
'fixed_ips': [{'ip_address': '11.10.0.3',
|
'device_owner': 'network:dhcp',
|
||||||
'subnet_id':
|
'fixed_ips': [{'ip_address': '11.10.0.3',
|
||||||
TEST.subnets.first().id}],
|
'subnet_id':
|
||||||
'id': 'a21dcd22-6733-cccc-aa32-22adafaf16a2',
|
TEST.subnets.first().id}],
|
||||||
'mac_address': '78:22:ff:1a:ba:23',
|
'id': 'a21dcd22-6733-cccc-aa32-22adafaf16a2',
|
||||||
'name': 'port5',
|
'mac_address': '78:22:ff:1a:ba:23',
|
||||||
'network_id': TEST.networks.first().id,
|
'name': 'port5',
|
||||||
'status': 'ACTIVE',
|
'network_id': TEST.networks.first().id,
|
||||||
'tenant_id': TEST.networks.first().tenant_id,
|
'status': 'ACTIVE',
|
||||||
'binding:vnic_type': 'normal',
|
'tenant_id': TEST.networks.first().tenant_id,
|
||||||
'binding:host_id': 'host'}
|
'binding:vnic_type': 'normal',
|
||||||
|
'binding:host_id': 'host',
|
||||||
|
'security_groups': [],
|
||||||
|
}
|
||||||
TEST.api_ports.add(port_dict)
|
TEST.api_ports.add(port_dict)
|
||||||
TEST.ports.add(neutron.Port(port_dict))
|
TEST.ports.add(neutron.Port(port_dict))
|
||||||
|
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Security group association per port is now shown in the port detail page.
|
||||||
|
In Neutron different security groups can be associated on different ports
|
||||||
|
of a same server instance, but previously it cannot be referred in Horizon.
|
Loading…
Reference in New Issue
Block a user