Merge "Show security groups in Port detail page"

This commit is contained in:
Jenkins 2017-06-20 12:16:12 +00:00 committed by Gerrit Code Review
commit 4a882c14b9
7 changed files with 179 additions and 99 deletions

View File

@ -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):

View File

@ -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)

View File

@ -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

View File

@ -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">

View File

@ -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']] +

View File

@ -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))

View File

@ -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.