Merge "Adds configuration support to associate firewall to routers"
This commit is contained in:
commit
e2b4a21fda
@ -2,6 +2,8 @@ horizon.firewalls = {
|
||||
user_decided_length: false,
|
||||
rules_selected: [],
|
||||
rules_available: [],
|
||||
routers_selected: [],
|
||||
routers_available: [],
|
||||
|
||||
getConsoleLog: function(via_user_submit) {
|
||||
var form_element = $("#tail_length"),
|
||||
@ -140,9 +142,117 @@ horizon.firewalls = {
|
||||
}).disableSelection();
|
||||
},
|
||||
|
||||
/*
|
||||
* Gets the html select element associated with a given
|
||||
* router id for router_id.
|
||||
**/
|
||||
get_router_element: function(router_id) {
|
||||
return $('li > label[for^="id_router_' + router_id + '"]');
|
||||
},
|
||||
|
||||
/*
|
||||
* Initializes an associative array of lists of the current
|
||||
* routers.
|
||||
**/
|
||||
init_router_list: function() {
|
||||
horizon.firewalls.routers_selected = [];
|
||||
horizon.firewalls.routers_available = [];
|
||||
$(this.get_router_element("")).each(function(){
|
||||
var $this = $(this);
|
||||
var $input = $this.children("input");
|
||||
var router_property = {
|
||||
name:$this.text().replace(/^\s+/,""),
|
||||
id:$input.attr("id"),
|
||||
value:$input.attr("value")
|
||||
};
|
||||
if($input.is(':checked')) {
|
||||
horizon.firewalls.routers_selected.push(router_property);
|
||||
} else {
|
||||
horizon.firewalls.routers_available.push(router_property);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
* Generates the HTML structure for a router that will be displayed
|
||||
* as a list item in the router list.
|
||||
**/
|
||||
generate_router_element: function(name, id, value) {
|
||||
var $li = $('<li>');
|
||||
$li.attr('name', value).html(name + '<em class="router_id">(' + value + ')</em><a href="#" class="btn btn-primary"></a>');
|
||||
return $li;
|
||||
},
|
||||
|
||||
/*
|
||||
* Generates the HTML structure for the router List.
|
||||
**/
|
||||
generate_routerlist_html: function() {
|
||||
var self = this;
|
||||
var updateForm = function() {
|
||||
var lists = $("#routerListId li").attr('data-index',100);
|
||||
var active_routers = $("#selected_router > li").map(function(){
|
||||
return $(this).attr("name");
|
||||
});
|
||||
$("#routerListId input:checkbox").removeAttr('checked');
|
||||
active_routers.each(function(index, value){
|
||||
$("#routerListId input:checkbox[value=" + value + "]")
|
||||
.prop('checked', true)
|
||||
.parents("li").attr('data-index',index);
|
||||
});
|
||||
$("#routerListId ul").html(
|
||||
lists.sort(function(a,b){
|
||||
if( $(a).data("index") < $(b).data("index")) { return -1; }
|
||||
if( $(a).data("index") > $(b).data("index")) { return 1; }
|
||||
return 0;
|
||||
})
|
||||
);
|
||||
};
|
||||
$("#routerListSortContainer").show();
|
||||
$("#routerListIdContainer").hide();
|
||||
self.init_router_list();
|
||||
// Make sure we don't duplicate the routers in the list
|
||||
$("#available_router").empty();
|
||||
$.each(self.routers_available, function(index, value){
|
||||
$("#available_router").append(self.generate_router_element(value.name, value.id, value.value));
|
||||
});
|
||||
// Make sure we don't duplicate the routers in the list
|
||||
$("#selected_router").empty();
|
||||
$.each(self.routers_selected, function(index, value){
|
||||
$("#selected_router").append(self.generate_router_element(value.name, value.id, value.value));
|
||||
});
|
||||
$(".routerlist > li > a.btn").click(function(e){
|
||||
var $this = $(this);
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if($this.parents("ul#available_router").length > 0) {
|
||||
$this.parent().appendTo($("#selected_router"));
|
||||
} else if ($this.parents("ul#selected_router").length > 0) {
|
||||
$this.parent().appendTo($("#available_router"));
|
||||
}
|
||||
updateForm();
|
||||
});
|
||||
if ($("#routerListId > div.form-group.error").length > 0) {
|
||||
var errortext = $("#routerListId > div.form-group.error").find("span.help-block").text();
|
||||
$("#selected_router_h4").before($('<div class="dynamic-error">').html(errortext));
|
||||
}
|
||||
$(".routerlist").sortable({
|
||||
connectWith: "ul.routerlist",
|
||||
placeholder: "ui-state-highlight",
|
||||
distance: 5,
|
||||
start:function(e,info){
|
||||
$("#selected_router").addClass("dragging");
|
||||
},
|
||||
stop:function(e,info){
|
||||
$("#selected_router").removeClass("dragging");
|
||||
updateForm();
|
||||
}
|
||||
}).disableSelection();
|
||||
},
|
||||
|
||||
workflow_init: function(modal) {
|
||||
// Initialise the drag and drop rule list
|
||||
horizon.firewalls.generate_rulelist_html();
|
||||
horizon.firewalls.generate_routerlist_html();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -16,6 +16,8 @@ from __future__ import absolute_import
|
||||
|
||||
from django.utils.datastructures import SortedDict
|
||||
|
||||
from horizon.utils import memoized
|
||||
|
||||
from openstack_dashboard.api import neutron
|
||||
|
||||
neutronclient = neutron.neutronclient
|
||||
@ -42,6 +44,11 @@ class Policy(neutron.NeutronAPIDictWrapper):
|
||||
class Firewall(neutron.NeutronAPIDictWrapper):
|
||||
"""Wrapper for neutron firewall."""
|
||||
|
||||
def __init__(self, apiresource):
|
||||
apiresource['admin_state'] = \
|
||||
'UP' if apiresource['admin_state_up'] else 'DOWN'
|
||||
super(Firewall, self).__init__(apiresource)
|
||||
|
||||
def __init__(self, apiresource):
|
||||
apiresource['admin_state'] = \
|
||||
'UP' if apiresource['admin_state_up'] else 'DOWN'
|
||||
@ -288,3 +295,18 @@ def firewall_update(request, firewall_id, **kwargs):
|
||||
firewall = neutronclient(request).update_firewall(
|
||||
firewall_id, body).get('firewall')
|
||||
return Firewall(firewall)
|
||||
|
||||
|
||||
@memoized.memoized
|
||||
def firewall_unassociated_routers_list(request, tenant_id):
|
||||
all_routers = neutron.router_list(request, tenant_id=tenant_id)
|
||||
tenant_firewalls = firewall_list_for_tenant(request, tenant_id=tenant_id)
|
||||
firewall_router_ids = [rid
|
||||
for fw in tenant_firewalls
|
||||
for rid in getattr(fw, 'router_ids', [])]
|
||||
|
||||
available_routers = [r for r in all_routers
|
||||
if r.id not in firewall_router_ids]
|
||||
available_routers = sorted(available_routers,
|
||||
key=lambda router: router.name_or_id)
|
||||
return available_routers
|
||||
|
@ -12,6 +12,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import abc
|
||||
import logging
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
@ -287,3 +288,94 @@ class RemoveRuleFromPolicy(forms.SelfHandlingForm):
|
||||
LOG.error(msg)
|
||||
redirect = reverse(self.failure_url)
|
||||
exceptions.handle(request, msg, redirect=redirect)
|
||||
|
||||
|
||||
class RouterInsertionFormBase(forms.SelfHandlingForm):
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(RouterInsertionFormBase, self).__init__(request, *args, **kwargs)
|
||||
try:
|
||||
router_choices = self.get_router_choices(request, kwargs)
|
||||
self.fields['router_ids'].choices = router_choices
|
||||
except Exception as e:
|
||||
msg = self.init_failure_msg % {'name': self.initial['name'],
|
||||
'reason': e}
|
||||
LOG.error(msg)
|
||||
redirect = reverse(self.failure_url)
|
||||
exceptions.handle(request, msg, redirect=redirect)
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_router_choices(self, request, kwargs):
|
||||
"""Return a list of selectable routers."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_new_router_ids(self, context):
|
||||
"""Return a new list of router IDs associated with the firewall."""
|
||||
|
||||
def handle(self, request, context):
|
||||
firewall_id = self.initial['firewall_id']
|
||||
firewall_name_or_id = self.initial['name'] or firewall_id
|
||||
try:
|
||||
body = {'router_ids': self.get_new_router_ids(context)}
|
||||
firewall = api.fwaas.firewall_update(request, firewall_id, **body)
|
||||
msg = self.success_msg % {'firewall': firewall_name_or_id}
|
||||
LOG.debug(msg)
|
||||
messages.success(request, msg)
|
||||
return firewall
|
||||
except Exception as e:
|
||||
msg = self.failure_msg % {'name': firewall_name_or_id, 'reason': e}
|
||||
LOG.error(msg)
|
||||
redirect = reverse(self.failure_url)
|
||||
exceptions.handle(request, msg, redirect=redirect)
|
||||
|
||||
|
||||
class AddRouterToFirewall(RouterInsertionFormBase):
|
||||
router_ids = forms.MultipleChoiceField(
|
||||
label=_("Add Routers"),
|
||||
required=False,
|
||||
widget=forms.CheckboxSelectMultiple(),
|
||||
help_text=_("Add selected router(s) to the firewall."))
|
||||
|
||||
failure_url = 'horizon:project:firewalls:index'
|
||||
success_msg = _('Router(s) was/were successfully added to firewall '
|
||||
'%(firewall)s.')
|
||||
failure_msg = _('Failed to add router(s) to firewall %(name)s: %(reason)s')
|
||||
init_failure_msg = _('Failed to retrieve available routers: %(reason)s')
|
||||
|
||||
def get_router_choices(self, request, kwargs):
|
||||
tenant_id = self.request.user.tenant_id
|
||||
routers_list = api.fwaas.firewall_unassociated_routers_list(
|
||||
request, tenant_id)
|
||||
return [(r.id, r.name_or_id) for r in routers_list]
|
||||
|
||||
def get_new_router_ids(self, context):
|
||||
existing_router_ids = self.initial['router_ids']
|
||||
add_router_ids = context['router_ids']
|
||||
return add_router_ids + existing_router_ids
|
||||
|
||||
|
||||
class RemoveRouterFromFirewall(RouterInsertionFormBase):
|
||||
router_ids = forms.MultipleChoiceField(
|
||||
label=_("Remove Routers"),
|
||||
required=False,
|
||||
widget=forms.CheckboxSelectMultiple(),
|
||||
help_text=_("Unselect the router(s) to be removed from firewall."))
|
||||
|
||||
failure_url = 'horizon:project:firewalls:index'
|
||||
success_msg = _('Router(s) was successfully removed from firewall '
|
||||
'%(firewall)s.')
|
||||
failure_msg = _('Failed to remove router(s) from firewall %(name)s: '
|
||||
'%(reason)s')
|
||||
init_failure_msg = _('Failed to retrieve current routers in firewall '
|
||||
'%(name)s: %(reason)s')
|
||||
|
||||
def get_router_choices(self, request, kwargs):
|
||||
tenant_id = self.request.user.tenant_id
|
||||
all_routers = api.neutron.router_list(request, tenant_id=tenant_id)
|
||||
current_routers = [r for r in all_routers
|
||||
if r['id'] in kwargs['initial']['router_ids']]
|
||||
return [(r.id, r.name_or_id) for r in current_routers]
|
||||
|
||||
def get_new_router_ids(self, context):
|
||||
# context[router_ids] is router IDs to be kept.
|
||||
return context['router_ids']
|
||||
|
@ -12,15 +12,21 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.template import defaultfilters as filters
|
||||
from django.utils.translation import pgettext_lazy
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ungettext_lazy
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tables
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard import policy
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AddRuleLink(tables.LinkAction):
|
||||
name = "addrule"
|
||||
@ -187,11 +193,59 @@ class RemoveRuleFromPolicyLink(policy.PolicyTargetMixin,
|
||||
return base_url
|
||||
|
||||
|
||||
class AddRouterToFirewallLink(policy.PolicyTargetMixin,
|
||||
tables.LinkAction):
|
||||
name = "addrouter"
|
||||
verbose_name = _("Add Router")
|
||||
classes = ("ajax-modal", "btn-update",)
|
||||
policy_rules = (("network", "get_firewall"),
|
||||
("network", "add_router"),)
|
||||
|
||||
def get_link_url(self, firewall):
|
||||
base_url = reverse("horizon:project:firewalls:addrouter",
|
||||
kwargs={'firewall_id': firewall.id})
|
||||
return base_url
|
||||
|
||||
def allowed(self, request, firewall):
|
||||
if not api.neutron.is_extension_supported(request,
|
||||
'fwaasrouterinsertion'):
|
||||
return False
|
||||
tenant_id = firewall['tenant_id']
|
||||
available_routers = api.fwaas.firewall_unassociated_routers_list(
|
||||
request, tenant_id)
|
||||
return bool(available_routers)
|
||||
|
||||
|
||||
class RemoveRouterFromFirewallLink(policy.PolicyTargetMixin,
|
||||
tables.LinkAction):
|
||||
name = "removerouter"
|
||||
verbose_name = _("Remove Router")
|
||||
classes = ("ajax-modal", "btn-update",)
|
||||
policy_rules = (("network", "get_firewall"),
|
||||
("network", "remove_router"),)
|
||||
|
||||
def get_link_url(self, firewall):
|
||||
base_url = reverse("horizon:project:firewalls:removerouter",
|
||||
kwargs={'firewall_id': firewall.id})
|
||||
return base_url
|
||||
|
||||
def allowed(self, request, firewall):
|
||||
if not api.neutron.is_extension_supported(request,
|
||||
'fwaasrouterinsertion'):
|
||||
return False
|
||||
return bool(firewall['router_ids'])
|
||||
|
||||
|
||||
def get_rules_name(datum):
|
||||
return ', '.join([rule.name or rule.id[:13]
|
||||
for rule in datum.rules])
|
||||
|
||||
|
||||
def get_routers_name(firewall):
|
||||
if firewall.routers:
|
||||
return ', '.join(router['name'] for router in firewall.routers)
|
||||
|
||||
|
||||
def get_policy_name(datum):
|
||||
if datum.policy:
|
||||
return datum.policy.name or datum.policy.id
|
||||
@ -287,6 +341,8 @@ class FirewallsTable(tables.DataTable):
|
||||
firewall_policy_id = tables.Column(get_policy_name,
|
||||
link=get_policy_link,
|
||||
verbose_name=_("Policy"))
|
||||
router_ids = tables.Column(get_routers_name,
|
||||
verbose_name=_("Associated Routers"))
|
||||
status = tables.Column("status",
|
||||
verbose_name=_("Status"),
|
||||
display_choices=STATUS_DISPLAY_CHOICES)
|
||||
@ -298,4 +354,19 @@ class FirewallsTable(tables.DataTable):
|
||||
name = "firewallstable"
|
||||
verbose_name = _("Firewalls")
|
||||
table_actions = (AddFirewallLink, DeleteFirewallLink)
|
||||
row_actions = (UpdateFirewallLink, DeleteFirewallLink)
|
||||
row_actions = (UpdateFirewallLink, DeleteFirewallLink,
|
||||
AddRouterToFirewallLink, RemoveRouterFromFirewallLink)
|
||||
|
||||
def __init__(self, request, data=None, needs_form_wrapper=None, **kwargs):
|
||||
super(FirewallsTable, self).__init__(
|
||||
request, data=data,
|
||||
needs_form_wrapper=needs_form_wrapper, **kwargs)
|
||||
try:
|
||||
if not api.neutron.is_extension_supported(request,
|
||||
'fwaasrouterinsertion'):
|
||||
del self.columns['router_ids']
|
||||
except Exception as e:
|
||||
msg = _('Failed to verify extension support %(reason)s') % {
|
||||
'reason': e}
|
||||
LOG.error(msg)
|
||||
exceptions.handle(request, msg)
|
||||
|
@ -75,6 +75,16 @@ class FirewallsTab(tabs.TableTab):
|
||||
tenant_id = self.request.user.tenant_id
|
||||
request = self.tab_group.request
|
||||
firewalls = api.fwaas.firewall_list_for_tenant(request, tenant_id)
|
||||
|
||||
if api.neutron.is_extension_supported(request,
|
||||
'fwaasrouterinsertion'):
|
||||
routers = api.neutron.router_list(request, tenant_id=tenant_id)
|
||||
|
||||
for fw in firewalls:
|
||||
router_list = [r for r in routers
|
||||
if r['id'] in fw['router_ids']]
|
||||
fw.get_dict()['routers'] = router_list
|
||||
|
||||
except Exception:
|
||||
firewalls = []
|
||||
exceptions.handle(self.tab_group.request,
|
||||
@ -127,11 +137,21 @@ class FirewallDetailsTab(tabs.Tab):
|
||||
fid = self.tab_group.kwargs['firewall_id']
|
||||
try:
|
||||
firewall = api.fwaas.firewall_get(request, fid)
|
||||
body = {'firewall': firewall}
|
||||
if api.neutron.is_extension_supported(request,
|
||||
'fwaasrouterinsertion'):
|
||||
tenant_id = self.request.user.tenant_id
|
||||
tenant_routers = api.neutron.router_list(request,
|
||||
tenant_id=tenant_id)
|
||||
router_ids = firewall.get_dict()['router_ids']
|
||||
routers = [r for r in tenant_routers
|
||||
if r['id'] in router_ids]
|
||||
body['routers'] = routers
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_('Unable to retrieve firewall details.'),
|
||||
redirect=self.failure_url)
|
||||
return {'firewall': firewall}
|
||||
return body
|
||||
|
||||
|
||||
class FirewallTabs(tabs.TabGroup):
|
||||
|
@ -0,0 +1,8 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "Choose the router(s) you want to add." %}</p>
|
||||
|
||||
{% endblock %}
|
@ -27,5 +27,17 @@
|
||||
|
||||
<dt>{% trans "Admin State Up" %}</dt>
|
||||
<dd>{{ firewall.admin_state_up|yesno|capfirst }}</dd>
|
||||
|
||||
<dt>{% trans "Routers" %}</dt>
|
||||
<dd>
|
||||
{% if routers %}
|
||||
{% for router in routers %}
|
||||
{% url 'horizon:project:routers:detail' router.id as router_url %}
|
||||
<a href="{{ router_url }}">{{ router.name|default:router.id}}</a><br>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% trans "-" %}
|
||||
{% endif %}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
@ -0,0 +1,7 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "Unselect the routers you want to disassociate from the firewall." %}</p>
|
||||
{% endblock %}
|
@ -0,0 +1,3 @@
|
||||
{% load i18n %}
|
||||
|
||||
<p>{% blocktrans %}Choose router(s) from Available Routers to Selected Routers by push button or drag and drop. {% endblocktrans %}</p>
|
@ -0,0 +1,45 @@
|
||||
{% load i18n %}
|
||||
|
||||
<noscript><h3>{{ step }}</h3></noscript>
|
||||
<table class="table-fixed" id="routerListSortContainer">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="actions">
|
||||
<h4 id="selected_router_h4">{% trans "Selected Routers" %}</h4>
|
||||
<ul id="selected_router" class="routerlist">
|
||||
</ul>
|
||||
<h4>{% trans "Available Routers" %}</h4>
|
||||
<ul id="available_router" class="routerlist">
|
||||
</ul>
|
||||
</td>
|
||||
<td class="help_text">
|
||||
{% include "project/firewalls/_update_router_help.html" %}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="table-fixed" id="routerListIdContainer">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="actions">
|
||||
<div id="routerListId">
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</div>
|
||||
</td>
|
||||
<td class="help_text">
|
||||
{{ step.get_help_text }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
if (typeof $ !== 'undefined') {
|
||||
horizon.firewalls.workflow_init($(".workflow"));
|
||||
} else {
|
||||
addHorizonLoadEvent(function() {
|
||||
horizon.firewalls.workflow_init($(".workflow"));
|
||||
});
|
||||
}
|
||||
</script>
|
@ -0,0 +1,7 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Add Router to Firewall" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'project/firewalls/_add_router_to_firewall.html' %}
|
||||
{% endblock %}
|
@ -0,0 +1,7 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Remove Router from Firewall" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'project/firewalls/_remove_router_from_firewall.html' %}
|
||||
{% endblock %}
|
@ -49,10 +49,21 @@ class FirewallTests(test.TestCase):
|
||||
INSERTRULE_PATH = 'horizon:%s:firewalls:insertrule' % DASHBOARD
|
||||
REMOVERULE_PATH = 'horizon:%s:firewalls:removerule' % DASHBOARD
|
||||
|
||||
def set_up_expect(self):
|
||||
ADDROUTER_PATH = 'horizon:%s:firewalls:addrouter' % DASHBOARD
|
||||
REMOVEROUTER_PATH = 'horizon:%s:firewalls:removerouter' % DASHBOARD
|
||||
|
||||
def set_up_expect(self, fwaas_router_extension=True):
|
||||
# retrieve rules
|
||||
tenant_id = self.tenant.id
|
||||
|
||||
api.neutron.is_extension_supported(
|
||||
IsA(http.HttpRequest), 'fwaasrouterinsertion'
|
||||
).AndReturn(fwaas_router_extension)
|
||||
|
||||
api.neutron.is_extension_supported(
|
||||
IsA(http.HttpRequest), 'fwaasrouterinsertion'
|
||||
).MultipleTimes().AndReturn(fwaas_router_extension)
|
||||
|
||||
api.fwaas.rule_list_for_tenant(
|
||||
IsA(http.HttpRequest),
|
||||
tenant_id).AndReturn(self.fw_rules.list())
|
||||
@ -67,9 +78,24 @@ class FirewallTests(test.TestCase):
|
||||
api.fwaas.firewall_list_for_tenant(
|
||||
IsA(http.HttpRequest), tenant_id).AndReturn(firewalls)
|
||||
|
||||
routers = self.routers.list()
|
||||
api.neutron.router_list(
|
||||
IsA(http.HttpRequest), tenant_id=tenant_id).AndReturn(routers)
|
||||
|
||||
api.neutron.router_list(
|
||||
IsA(http.HttpRequest), tenant_id=tenant_id). \
|
||||
MultipleTimes().AndReturn(routers)
|
||||
|
||||
api.fwaas.firewall_list_for_tenant(
|
||||
IsA(http.HttpRequest), tenant_id='1'). \
|
||||
MultipleTimes().AndReturn(firewalls)
|
||||
|
||||
def set_up_expect_with_exception(self):
|
||||
tenant_id = self.tenant.id
|
||||
|
||||
api.neutron.is_extension_supported(
|
||||
IsA(http.HttpRequest), 'fwaasrouterinsertion').AndReturn(True)
|
||||
|
||||
api.fwaas.rule_list_for_tenant(
|
||||
IsA(http.HttpRequest),
|
||||
tenant_id).AndRaise(self.exceptions.neutron)
|
||||
@ -82,7 +108,9 @@ class FirewallTests(test.TestCase):
|
||||
|
||||
@test.create_stubs({api.fwaas: ('firewall_list_for_tenant',
|
||||
'policy_list_for_tenant',
|
||||
'rule_list_for_tenant')}, )
|
||||
'rule_list_for_tenant'),
|
||||
api.neutron: ('is_extension_supported',
|
||||
'router_list',), })
|
||||
def test_index_firewalls(self):
|
||||
self.set_up_expect()
|
||||
|
||||
@ -98,9 +126,14 @@ class FirewallTests(test.TestCase):
|
||||
self.assertEqual(len(res.context['table'].data),
|
||||
len(self.firewalls.list()))
|
||||
|
||||
# TODO(absubram): Change test_index_firewalls for with and without
|
||||
# router extensions.
|
||||
|
||||
@test.create_stubs({api.fwaas: ('firewall_list_for_tenant',
|
||||
'policy_list_for_tenant',
|
||||
'rule_list_for_tenant')}, )
|
||||
'rule_list_for_tenant'),
|
||||
api.neutron: ('is_extension_supported',
|
||||
'router_list',), })
|
||||
def test_index_policies(self):
|
||||
self.set_up_expect()
|
||||
|
||||
@ -119,7 +152,9 @@ class FirewallTests(test.TestCase):
|
||||
|
||||
@test.create_stubs({api.fwaas: ('firewall_list_for_tenant',
|
||||
'policy_list_for_tenant',
|
||||
'rule_list_for_tenant')}, )
|
||||
'rule_list_for_tenant'),
|
||||
api.neutron: ('is_extension_supported',
|
||||
'router_list',), })
|
||||
def test_index_rules(self):
|
||||
self.set_up_expect()
|
||||
|
||||
@ -138,7 +173,8 @@ class FirewallTests(test.TestCase):
|
||||
|
||||
@test.create_stubs({api.fwaas: ('firewall_list_for_tenant',
|
||||
'policy_list_for_tenant',
|
||||
'rule_list_for_tenant')}, )
|
||||
'rule_list_for_tenant'),
|
||||
api.neutron: ('is_extension_supported',), })
|
||||
def test_index_exception_firewalls(self):
|
||||
self.set_up_expect_with_exception()
|
||||
|
||||
@ -157,7 +193,8 @@ class FirewallTests(test.TestCase):
|
||||
|
||||
@test.create_stubs({api.fwaas: ('firewall_list_for_tenant',
|
||||
'policy_list_for_tenant',
|
||||
'rule_list_for_tenant')}, )
|
||||
'rule_list_for_tenant'),
|
||||
api.neutron: ('is_extension_supported',), })
|
||||
def test_index_exception_policies(self):
|
||||
self.set_up_expect_with_exception()
|
||||
|
||||
@ -177,7 +214,8 @@ class FirewallTests(test.TestCase):
|
||||
|
||||
@test.create_stubs({api.fwaas: ('firewall_list_for_tenant',
|
||||
'policy_list_for_tenant',
|
||||
'rule_list_for_tenant')}, )
|
||||
'rule_list_for_tenant'),
|
||||
api.neutron: ('is_extension_supported',), })
|
||||
def test_index_exception_rules(self):
|
||||
self.set_up_expect_with_exception()
|
||||
|
||||
@ -301,18 +339,31 @@ class FirewallTests(test.TestCase):
|
||||
|
||||
self.assertFormErrors(res, 1)
|
||||
|
||||
@test.create_stubs({api.fwaas: ('firewall_create',
|
||||
'policy_list_for_tenant'), })
|
||||
def test_add_firewall_post(self):
|
||||
def _test_add_firewall_post(self, router_extension=False):
|
||||
firewall = self.firewalls.first()
|
||||
policies = self.fw_policies.list()
|
||||
tenant_id = self.tenant.id
|
||||
if router_extension:
|
||||
routers = self.routers.list()
|
||||
firewalls = self.firewalls.list()
|
||||
|
||||
form_data = {'name': firewall.name,
|
||||
'description': firewall.description,
|
||||
'firewall_policy_id': firewall.firewall_policy_id,
|
||||
'shared': firewall.shared,
|
||||
'admin_state_up': firewall.admin_state_up
|
||||
}
|
||||
if router_extension:
|
||||
form_data['router_ids'] = firewall.router_ids
|
||||
api.neutron.router_list(
|
||||
IsA(http.HttpRequest), tenant_id=tenant_id).AndReturn(routers)
|
||||
api.fwaas.firewall_list_for_tenant(
|
||||
IsA(http.HttpRequest),
|
||||
tenant_id=tenant_id).AndReturn(firewalls)
|
||||
|
||||
api.neutron.is_extension_supported(
|
||||
IsA(http.HttpRequest),
|
||||
'fwaasrouterinsertion').AndReturn(router_extension)
|
||||
api.fwaas.policy_list_for_tenant(
|
||||
IsA(http.HttpRequest), tenant_id).AndReturn(policies)
|
||||
api.fwaas.firewall_create(
|
||||
@ -326,7 +377,25 @@ class FirewallTests(test.TestCase):
|
||||
self.assertRedirectsNoFollow(res, str(self.INDEX_URL))
|
||||
|
||||
@test.create_stubs({api.fwaas: ('firewall_create',
|
||||
'policy_list_for_tenant'), })
|
||||
'policy_list_for_tenant',),
|
||||
api.neutron: ('is_extension_supported',), })
|
||||
def test_add_firewall_post(self):
|
||||
self._test_add_firewall_post()
|
||||
|
||||
# @test.create_stubs({api.fwaas: ('firewall_create',
|
||||
# 'policy_list_for_tenant',
|
||||
# 'firewall_list_for_tenant',),
|
||||
# api.neutron: ('is_extension_supported',
|
||||
# 'router_list'), })
|
||||
# def test_add_firewall_post_with_router_extension(self):
|
||||
# self._test_add_firewall_post(router_extension=True)
|
||||
# TODO(absubram): Fix test_add_firewall_post_with_router_extension
|
||||
# It currently fails because views.py is not
|
||||
# initializing the AddRouter workflow?
|
||||
|
||||
@test.create_stubs({api.fwaas: ('firewall_create',
|
||||
'policy_list_for_tenant',),
|
||||
api.neutron: ('is_extension_supported',), })
|
||||
def test_add_firewall_post_with_error(self):
|
||||
firewall = self.firewalls.first()
|
||||
policies = self.fw_policies.list()
|
||||
@ -337,6 +406,9 @@ class FirewallTests(test.TestCase):
|
||||
'shared': firewall.shared,
|
||||
'admin_state_up': firewall.admin_state_up
|
||||
}
|
||||
api.neutron.is_extension_supported(
|
||||
IsA(http.HttpRequest),
|
||||
'fwaasrouterinsertion').AndReturn(False)
|
||||
api.fwaas.policy_list_for_tenant(
|
||||
IsA(http.HttpRequest), tenant_id).AndReturn(policies)
|
||||
|
||||
@ -618,10 +690,75 @@ class FirewallTests(test.TestCase):
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, str(self.INDEX_URL))
|
||||
|
||||
@test.create_stubs({api.fwaas: ('firewall_get',
|
||||
'firewall_list_for_tenant',
|
||||
'firewall_update',
|
||||
'firewall_unassociated_routers_list')})
|
||||
def test_firewall_add_router(self):
|
||||
tenant_id = self.tenant.id
|
||||
firewall = self.firewalls.first()
|
||||
routers = self.routers.list()
|
||||
|
||||
existing_router_ids = firewall.router_ids
|
||||
add_router_ids = [routers[1].id]
|
||||
|
||||
form_data = {'router_ids': add_router_ids}
|
||||
post_data = {'router_ids': add_router_ids + existing_router_ids}
|
||||
|
||||
api.fwaas.firewall_get(
|
||||
IsA(http.HttpRequest), firewall.id).AndReturn(firewall)
|
||||
api.fwaas.firewall_unassociated_routers_list(
|
||||
IsA(http.HttpRequest), tenant_id).AndReturn(routers)
|
||||
|
||||
firewall.router_ids = [add_router_ids, existing_router_ids]
|
||||
|
||||
api.fwaas.firewall_update(
|
||||
IsA(http.HttpRequest),
|
||||
firewall.id, **post_data).AndReturn(firewall)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.post(
|
||||
reverse(self.ADDROUTER_PATH, args=(firewall.id,)), form_data)
|
||||
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, str(self.INDEX_URL))
|
||||
|
||||
@test.create_stubs({api.fwaas: ('firewall_get',
|
||||
'firewall_update',
|
||||
'firewall_unassociated_routers_list'),
|
||||
api.neutron: ('router_list',), })
|
||||
def test_firewall_remove_router(self):
|
||||
firewall = self.firewalls.first()
|
||||
tenant_id = self.tenant.id
|
||||
routers = self.routers.list()
|
||||
existing_router_ids = firewall.router_ids
|
||||
|
||||
form_data = {'router_ids': existing_router_ids}
|
||||
|
||||
api.fwaas.firewall_get(
|
||||
IsA(http.HttpRequest), firewall.id).AndReturn(firewall)
|
||||
api.neutron.router_list(
|
||||
IsA(http.HttpRequest), tenant_id=tenant_id).AndReturn(routers)
|
||||
firewall.router_ids = []
|
||||
api.fwaas.firewall_update(
|
||||
IsA(http.HttpRequest),
|
||||
firewall.id, **form_data).AndReturn(firewall)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.post(
|
||||
reverse(self.REMOVEROUTER_PATH, args=(firewall.id,)), form_data)
|
||||
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, str(self.INDEX_URL))
|
||||
|
||||
@test.create_stubs({api.fwaas: ('firewall_list_for_tenant',
|
||||
'policy_list_for_tenant',
|
||||
'rule_list_for_tenant',
|
||||
'rule_delete')})
|
||||
'rule_delete'),
|
||||
api.neutron: ('is_extension_supported',
|
||||
'router_list',), })
|
||||
def test_delete_rule(self):
|
||||
self.set_up_expect()
|
||||
rule = self.fw_rules.first()
|
||||
@ -636,7 +773,9 @@ class FirewallTests(test.TestCase):
|
||||
@test.create_stubs({api.fwaas: ('firewall_list_for_tenant',
|
||||
'policy_list_for_tenant',
|
||||
'rule_list_for_tenant',
|
||||
'policy_delete')})
|
||||
'policy_delete'),
|
||||
api.neutron: ('is_extension_supported',
|
||||
'router_list',), })
|
||||
def test_delete_policy(self):
|
||||
self.set_up_expect()
|
||||
policy = self.fw_policies.first()
|
||||
@ -651,7 +790,9 @@ class FirewallTests(test.TestCase):
|
||||
@test.create_stubs({api.fwaas: ('firewall_list_for_tenant',
|
||||
'policy_list_for_tenant',
|
||||
'rule_list_for_tenant',
|
||||
'firewall_delete')})
|
||||
'firewall_delete'),
|
||||
api.neutron: ('is_extension_supported',
|
||||
'router_list',), })
|
||||
def test_delete_firewall(self):
|
||||
self.set_up_expect()
|
||||
fwl = self.firewalls.first()
|
||||
|
@ -39,5 +39,9 @@ urlpatterns = patterns(
|
||||
views.RuleDetailsView.as_view(), name='ruledetails'),
|
||||
url(r'^policy/(?P<policy_id>[^/]+)/$',
|
||||
views.PolicyDetailsView.as_view(), name='policydetails'),
|
||||
url(r'^addrouter/(?P<firewall_id>[^/]+)/$',
|
||||
views.AddRouterToFirewallView.as_view(), name='addrouter'),
|
||||
url(r'^removerouter/(?P<firewall_id>[^/]+)/$',
|
||||
views.RemoveRouterFromFirewallView.as_view(), name='removerouter'),
|
||||
url(r'^firewall/(?P<firewall_id>[^/]+)/$',
|
||||
views.FirewallDetailsView.as_view(), name='firewalldetails'))
|
||||
|
@ -33,7 +33,9 @@ from openstack_dashboard.dashboards.project.firewalls \
|
||||
from openstack_dashboard.dashboards.project.firewalls \
|
||||
import workflows as fw_workflows
|
||||
|
||||
AddRouterToFirewall = fw_forms.AddRouterToFirewall
|
||||
InsertRuleToPolicy = fw_forms.InsertRuleToPolicy
|
||||
RemoveRouterFromFirewall = fw_forms.RemoveRouterFromFirewall
|
||||
RemoveRuleFromPolicy = fw_forms.RemoveRuleFromPolicy
|
||||
UpdateFirewall = fw_forms.UpdateFirewall
|
||||
UpdatePolicy = fw_forms.UpdatePolicy
|
||||
@ -104,6 +106,13 @@ class AddFirewallView(workflows.WorkflowView):
|
||||
template_name = "project/firewalls/addfirewall.html"
|
||||
page_title = _("Add New Firewall")
|
||||
|
||||
def get_workflow(self):
|
||||
if api.neutron.is_extension_supported(self.request,
|
||||
'fwaasrouterinsertion'):
|
||||
AddFirewall.register(fw_workflows.SelectRoutersStep)
|
||||
workflow = super(AddFirewallView, self).get_workflow()
|
||||
return workflow
|
||||
|
||||
|
||||
class FireWallDetailTabs(tabs.TabView):
|
||||
template_name = 'project/firewalls/details_tabs.html'
|
||||
@ -318,3 +327,53 @@ class RemoveRuleFromPolicyView(forms.ModalFormView):
|
||||
initial = policy.get_dict()
|
||||
initial['policy_id'] = initial['id']
|
||||
return initial
|
||||
|
||||
|
||||
class RouterCommonView(forms.ModalFormView):
|
||||
form_id = "update_firewall_form"
|
||||
context_object_name = 'firewall'
|
||||
submit_label = _("Save Changes")
|
||||
success_url = reverse_lazy("horizon:project:firewalls:index")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(RouterCommonView,
|
||||
self).get_context_data(**kwargs)
|
||||
context["firewall_id"] = self.kwargs['firewall_id']
|
||||
args = (self.kwargs['firewall_id'],)
|
||||
context['submit_url'] = reverse(self.submit_url, args=args)
|
||||
obj = self._get_object()
|
||||
if obj:
|
||||
context['name'] = obj.name_or_id
|
||||
return context
|
||||
|
||||
@memoized.memoized_method
|
||||
def _get_object(self, *args, **kwargs):
|
||||
firewall_id = self.kwargs['firewall_id']
|
||||
try:
|
||||
firewall = api.fwaas.firewall_get(self.request, firewall_id)
|
||||
return firewall
|
||||
except Exception:
|
||||
redirect = self.success_url
|
||||
msg = _('Unable to retrieve firewall details.')
|
||||
exceptions.handle(self.request, msg, redirect=redirect)
|
||||
|
||||
def get_initial(self):
|
||||
firewall = self._get_object()
|
||||
initial = firewall.get_dict()
|
||||
return initial
|
||||
|
||||
|
||||
class AddRouterToFirewallView(RouterCommonView):
|
||||
form_class = AddRouterToFirewall
|
||||
modal_header = _("Add Router to Firewall")
|
||||
template_name = "project/firewalls/add_router_to_firewall.html"
|
||||
submit_url = "horizon:project:firewalls:addrouter"
|
||||
page_title = _("Add Router to Firewall")
|
||||
|
||||
|
||||
class RemoveRouterFromFirewallView(RouterCommonView):
|
||||
form_class = RemoveRouterFromFirewall
|
||||
modal_header = _("Remove Router from Firewall")
|
||||
template_name = "project/firewalls/remove_router_from_firewall.html"
|
||||
submit_url = "horizon:project:firewalls:removerouter"
|
||||
page_title = _("Remove Router from Firewall")
|
||||
|
@ -168,6 +168,51 @@ class SelectRulesStep(workflows.Step):
|
||||
return context
|
||||
|
||||
|
||||
class SelectRoutersAction(workflows.Action):
|
||||
router = forms.MultipleChoiceField(
|
||||
label=_("Routers"),
|
||||
required=False,
|
||||
widget=forms.CheckboxSelectMultiple(),
|
||||
help_text=_("Create a firewall with selected routers."))
|
||||
|
||||
class Meta(object):
|
||||
name = _("Routers")
|
||||
permissions = ('openstack.services.network',)
|
||||
help_text = _("Select routers for your firewall.")
|
||||
|
||||
def populate_router_choices(self, request, context):
|
||||
try:
|
||||
tenant_id = self.request.user.tenant_id
|
||||
routers_list = api.fwaas.firewall_unassociated_routers_list(
|
||||
request, tenant_id)
|
||||
|
||||
except Exception as e:
|
||||
routers_list = []
|
||||
exceptions.handle(request,
|
||||
_('Unable to retrieve routers (%(error)s).') % {
|
||||
'error': str(e)})
|
||||
routers_list = [(router.id, router.name_or_id)
|
||||
for router in routers_list]
|
||||
return routers_list
|
||||
|
||||
|
||||
class SelectRoutersStep(workflows.Step):
|
||||
action_class = SelectRoutersAction
|
||||
template_name = "project/firewalls/_update_routers.html"
|
||||
contributes = ("router_ids", "all_routers_selected",
|
||||
"Select No Routers")
|
||||
|
||||
def contribute(self, data, context):
|
||||
if data:
|
||||
routers = self.workflow.request.POST.getlist("router")
|
||||
if routers:
|
||||
routers = [r for r in routers if r != '']
|
||||
context['router_ids'] = routers
|
||||
else:
|
||||
context['router_ids'] = []
|
||||
return context
|
||||
|
||||
|
||||
class AddPolicyAction(workflows.Action):
|
||||
name = forms.CharField(max_length=80,
|
||||
label=_("Name"))
|
||||
@ -293,7 +338,7 @@ class AddFirewall(workflows.Workflow):
|
||||
# involve more complex configuration over time. Hence,
|
||||
# a workflow instead of a single form is used for
|
||||
# firewall_rule add to be ready for future extension.
|
||||
default_steps = (AddFirewallStep,)
|
||||
default_steps = (AddFirewallStep, )
|
||||
|
||||
def format_status_message(self, message):
|
||||
return message % self.context.get('name')
|
||||
|
@ -1775,6 +1775,116 @@ label.log-length {
|
||||
|
||||
}
|
||||
|
||||
/* Styling for draged firewall router object */
|
||||
#routerListSortContainer {
|
||||
display: none;
|
||||
}
|
||||
.routerlist {
|
||||
padding: 6px;
|
||||
background: #eee;
|
||||
border: 1px solid $border-color;
|
||||
min-height: 2em;
|
||||
width: auto !important;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
li {
|
||||
width: 226px;
|
||||
list-style-type: none;
|
||||
margin: 6px auto;
|
||||
padding: 3px;
|
||||
background: $body-bg;
|
||||
border: 1px solid $border-color;
|
||||
line-height: 18px;
|
||||
border-radius: 3px;
|
||||
cursor: move;
|
||||
padding-left: 23px;
|
||||
background: $body-bg url(../img/drag.png) no-repeat 11px 50%;
|
||||
em {
|
||||
font-size: 0.5em;
|
||||
line-height: 1em;
|
||||
color:#999;
|
||||
font-style: normal;
|
||||
margin-left: 0.8em;
|
||||
}
|
||||
i {
|
||||
margin-right: 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
a.btn {
|
||||
@include box-sizing(border-box);
|
||||
font-size: 11px;
|
||||
line-height: 12px;
|
||||
padding: 2px 5px 3px;
|
||||
margin-right: 1px;
|
||||
width: 18px;
|
||||
text-align: center;
|
||||
//position: absolute;
|
||||
right:5px;
|
||||
vertical-align: middle;
|
||||
float: right;
|
||||
&:before {
|
||||
content: "+";
|
||||
}
|
||||
}
|
||||
}
|
||||
li.ui-sortable-helper {
|
||||
background-color: #def;
|
||||
}
|
||||
li.ui-state-highlight {
|
||||
border: 1px dotted $border-color;
|
||||
background: #efefef;
|
||||
height: 0.5em;
|
||||
}
|
||||
li:after {
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
font-size: 0;
|
||||
content: " ";
|
||||
clear: both;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
#selected_router {
|
||||
margin-bottom: 1.5em;
|
||||
counter-reset:v1 0;
|
||||
background: #edf9ff;
|
||||
border:1px solid $border-color;
|
||||
li {
|
||||
position: relative;
|
||||
a.btn {
|
||||
&:before {
|
||||
content: "-";
|
||||
}
|
||||
}
|
||||
}
|
||||
li:before {
|
||||
content:"router:"counter(v1);
|
||||
counter-increment:v1;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
background: $gray;
|
||||
color:$body-bg;
|
||||
font-size: 90%;
|
||||
padding: 0px 4px;
|
||||
vertical-align: middle;
|
||||
border-radius: 2px;
|
||||
position: absolute;
|
||||
left: -2em;
|
||||
}
|
||||
&.dragging {
|
||||
li:before {
|
||||
content:"router:";
|
||||
background-color:rgba(102,102,102,0.5);
|
||||
padding-right: 10px;
|
||||
}
|
||||
li.ui-state-highlight:before {
|
||||
content:"";
|
||||
background:transparent;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/**** Sort Indicator ****/
|
||||
.tablesorter thead tr th
|
||||
{
|
||||
|
@ -346,6 +346,9 @@ class FwaasApiTests(test.APITestCase):
|
||||
self.assertEqual(exp_firewall.firewall_policy_id, ret_val.policy.id)
|
||||
self.assertEqual(exp_firewall.policy.name, ret_val.policy.name)
|
||||
|
||||
# TODO(absubram) : Add API tests for firewall_create with routers,
|
||||
# add router to firewal and remove router from fw.
|
||||
|
||||
@test.create_stubs({neutronclient: ('list_firewalls',
|
||||
'list_firewall_policies')})
|
||||
def test_firewall_list(self):
|
||||
|
@ -967,6 +967,7 @@ def data(TEST):
|
||||
'firewall_policy_id':
|
||||
'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'name': 'firewall1',
|
||||
'router_ids': [TEST.routers.first().id],
|
||||
'description': 'firewall description',
|
||||
'status': 'PENDING_CREATE',
|
||||
'shared': True,
|
||||
@ -975,6 +976,7 @@ def data(TEST):
|
||||
|
||||
fw1 = fwaas.Firewall(copy.deepcopy(fw1_dict))
|
||||
fw1._apidict['policy'] = policy1
|
||||
fw1._apidict['routers'] = [TEST.routers.first()]
|
||||
TEST.firewalls.add(fw1)
|
||||
|
||||
# 2nd firewall (no name)
|
||||
|
Loading…
Reference in New Issue
Block a user