Port Forwardings tab
Change-Id: Iaf964a6c59ef1818e32202a9980df8e83bbbceed
This commit is contained in:
parent
a814981a9f
commit
47a3e7f204
|
@ -29,3 +29,4 @@ tags
|
|||
ghostdriver.log
|
||||
.testrepository
|
||||
.idea
|
||||
.vscode
|
||||
|
|
|
@ -4,9 +4,10 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
import six
|
||||
from django.conf import settings
|
||||
from horizon import exceptions
|
||||
import six
|
||||
from openstack_dashboard.api import base
|
||||
|
||||
def get_request_page_size(request, limit=None):
|
||||
default_limit = getattr(settings, 'API_RESULT_LIMIT', 1000)
|
||||
|
@ -15,3 +16,9 @@ def get_request_page_size(request, limit=None):
|
|||
except Exception:
|
||||
default_page_size = getattr(settings, 'API_RESULT_PAGE_SIZE', 20)
|
||||
return request.session.get('horizon_pagesize', default_page_size)
|
||||
|
||||
|
||||
def is_TiS_region(request):
|
||||
if not base.is_service_enabled(request, 'platform'):
|
||||
return False
|
||||
return True
|
|
@ -0,0 +1,33 @@
|
|||
from openstack_dashboard.api import base
|
||||
from openstack_dashboard.api.neutron import *
|
||||
|
||||
class PortForwardingRule(base.APIDictWrapper):
|
||||
pass
|
||||
|
||||
def portforwarding_list(request, **params):
|
||||
rules = (neutronclient(request).
|
||||
list_portforwardings(**params).get('portforwardings'))
|
||||
return [PortForwardingRule(r) for r in rules]
|
||||
|
||||
|
||||
def portforwarding_get(request, portforwarding_id):
|
||||
rule = (neutronclient(request).
|
||||
show_portforwarding(portforwarding_id).get('portforwarding'))
|
||||
return PortForwardingRule(rule)
|
||||
|
||||
|
||||
def portforwarding_create(request, **kwargs):
|
||||
body = {'portforwarding': kwargs}
|
||||
rule = neutronclient(request).create_portforwarding(body=body)
|
||||
return PortForwardingRule(rule)
|
||||
|
||||
|
||||
def portforwarding_update(request, portforwarding_id, **kwargs):
|
||||
body = {'portforwarding': kwargs}
|
||||
rule = neutronclient(request).update_portforwarding(
|
||||
portforwarding_id, body=body).get('portforwarding')
|
||||
return PortForwardingRule(rule)
|
||||
|
||||
|
||||
def portforwarding_delete(request, portforwarding_id):
|
||||
return neutronclient(request).delete_portforwarding(portforwarding_id)
|
|
@ -0,0 +1,176 @@
|
|||
# Copyright (c) 2013-2015,2017 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
import logging
|
||||
import netaddr
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.datastructures import SortedDict
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
from horizon.utils import memoized
|
||||
from openstack_dashboard import api
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
PROTOCOL_CHOICES = [("tcp", "TCP"),
|
||||
("udp", "UDP"),
|
||||
("udp-lite", "UDP-Lite"),
|
||||
("sctp", "SCTP"),
|
||||
("dccp", "DCCP")]
|
||||
|
||||
|
||||
class AddPortForwardingRule(forms.SelfHandlingForm):
|
||||
inside_addr = forms.ChoiceField(
|
||||
label=_("IP Address"),
|
||||
required=True,
|
||||
initial="Select an IP address",
|
||||
help_text=_("Specify a private IP address which will be the "
|
||||
"destination of the forwarding rule "
|
||||
"(e.g., 192.168.0.254)."))
|
||||
inside_port = forms.IntegerField(
|
||||
label=_("Private Port"), required=True, initial="",
|
||||
min_value=1, max_value=65535,
|
||||
help_text=_("Specify a private layer4 protocol port number"))
|
||||
outside_port = forms.IntegerField(
|
||||
label=_("Public Port"), required=True, initial="",
|
||||
min_value=1, max_value=65535,
|
||||
help_text=_("Specify a public layer4 protocol port number"))
|
||||
protocol = forms.ChoiceField(
|
||||
label=_("Protocol"), required=True, initial="Select a protocol")
|
||||
description = forms.CharField(label=_("Description"), required=False)
|
||||
router_name = forms.CharField(label=_("Router Name"),
|
||||
widget=forms.TextInput(
|
||||
attrs={'readonly': 'readonly'}))
|
||||
router_id = forms.CharField(label=_("Router ID"),
|
||||
widget=forms.TextInput(
|
||||
attrs={'readonly': 'readonly'}))
|
||||
failure_url = 'horizon:project:routers:detail'
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(AddPortForwardingRule, self).__init__(request, *args, **kwargs)
|
||||
self.router_id = kwargs['initial']['router_id']
|
||||
self.tenant_id = kwargs['initial']['tenant_id']
|
||||
address_choices = self.populate_inside_addr_choices(request)
|
||||
self.fields['inside_addr'].choices = address_choices
|
||||
self.fields['protocol'].choices = PROTOCOL_CHOICES
|
||||
|
||||
@memoized.memoized_method
|
||||
def _get_connected_ipv4_networks(self, request, router_id):
|
||||
networks = set()
|
||||
subnets = set()
|
||||
ports = api.neutron.port_list(request, device_id=router_id)
|
||||
for p in ports:
|
||||
if p.device_owner not in api.neutron.ROUTER_INTERFACE_OWNERS:
|
||||
continue
|
||||
if not p.fixed_ips:
|
||||
continue
|
||||
networks.add(p.network_id)
|
||||
for ip in p.fixed_ips:
|
||||
if netaddr.valid_ipv4(ip['ip_address']):
|
||||
subnets.add(ip['subnet_id'])
|
||||
return (list(networks), list(subnets))
|
||||
|
||||
@memoized.memoized_method
|
||||
def _get_available_addresses(self, request, network_ids, subnet_ids):
|
||||
address_list = []
|
||||
for network_id in network_ids:
|
||||
ports = api.neutron.port_list(request, network_id=network_id)
|
||||
for p in ports:
|
||||
if p.device_owner.startswith('network:'):
|
||||
continue
|
||||
for ip in p.fixed_ips:
|
||||
if ip['subnet_id'] in subnet_ids:
|
||||
record = (ip['ip_address'], p.id, p.device_id)
|
||||
address_list.append(record)
|
||||
return address_list
|
||||
|
||||
@memoized.memoized_method
|
||||
def _get_servers(self, request):
|
||||
search_opts = {'project_id': self.tenant_id}
|
||||
servers, has_more = api.nova.server_list(
|
||||
self.request, search_opts=search_opts)
|
||||
server_dict = SortedDict([(s.id, s.name) for s in servers])
|
||||
return server_dict
|
||||
|
||||
def populate_inside_addr_choices(self, request):
|
||||
network_ids, subnet_ids = self._get_connected_ipv4_networks(
|
||||
request, self.router_id)
|
||||
addresses = self._get_available_addresses(
|
||||
request, network_ids, subnet_ids)
|
||||
servers = self._get_servers(request)
|
||||
choices = []
|
||||
for ip_address, port_id, device_id in addresses:
|
||||
server = servers.get(device_id)
|
||||
display_name = ip_address
|
||||
if server:
|
||||
display_name += " : {}".format(server)
|
||||
choices.append((ip_address, display_name))
|
||||
choices.insert(0, ("", "Select an IP address"))
|
||||
return choices
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
router_id = data['router_id']
|
||||
params = {'router_id': data['router_id'],
|
||||
'inside_addr': data['inside_addr'],
|
||||
'inside_port': data['inside_port'],
|
||||
'outside_port': data['outside_port'],
|
||||
'protocol': data['protocol'].lower()}
|
||||
if 'description' in data:
|
||||
params['description'] = data['description']
|
||||
api.neutron.portforwarding_create(request, **params)
|
||||
except Exception as e:
|
||||
self._handle_error(request, router_id, e)
|
||||
msg = _('Port forwarding rule added')
|
||||
LOG.debug(msg)
|
||||
messages.success(request, msg)
|
||||
return True
|
||||
|
||||
def _handle_error(self, request, router_id, reason):
|
||||
msg = _('Failed to add port forwarding rule: %s') % reason
|
||||
LOG.info(msg)
|
||||
redirect = reverse(self.failure_url, args=[router_id])
|
||||
exceptions.handle(request, msg, redirect=redirect)
|
||||
|
||||
|
||||
class UpdatePortForwardingRule(AddPortForwardingRule):
|
||||
|
||||
portforwarding_id = forms.CharField(
|
||||
label=_("ID"), required=False,
|
||||
widget=forms.TextInput(attrs={'readonly': 'readonly'}))
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(UpdatePortForwardingRule, self).__init__(
|
||||
request, *args, **kwargs)
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
portforwarding_id = data['portforwarding_id']
|
||||
router_id = data['router_id']
|
||||
params = {'inside_addr': data['inside_addr'],
|
||||
'inside_port': data['inside_port'],
|
||||
'outside_port': data['outside_port'],
|
||||
'protocol': data['protocol'].lower()}
|
||||
if 'description' in data:
|
||||
params['description'] = data['description']
|
||||
api.neutron.portforwarding_update(request,
|
||||
portforwarding_id,
|
||||
**params)
|
||||
except Exception as e:
|
||||
self._handle_error(request, router_id, e)
|
||||
msg = _('Port forwarding rule updated')
|
||||
LOG.debug(msg)
|
||||
messages.success(request, msg)
|
||||
return True
|
||||
|
||||
def _handle_error(self, request, router_id, reason):
|
||||
msg = _('Failed to update port forwarding rule: %s') % reason
|
||||
LOG.info(msg)
|
||||
redirect = reverse(self.failure_url, args=[router_id])
|
||||
exceptions.handle(request, msg, redirect=redirect)
|
|
@ -0,0 +1,110 @@
|
|||
# Copyright (c) 2013-2015 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
import logging
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
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 AddPortForwardingRule(policy.PolicyTargetMixin, tables.LinkAction):
|
||||
name = "createportforwardingrule"
|
||||
verbose_name = _("Add Rule")
|
||||
url = "horizon:project:routers:addportforwardingrule"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
policy_rules = (("network", "create_portforwarding"),)
|
||||
|
||||
def get_link_url(self, datum=None):
|
||||
router_id = self.table.kwargs['router_id']
|
||||
return reverse(self.url, args=(router_id,))
|
||||
|
||||
|
||||
class UpdatePortForwardingRule(policy.PolicyTargetMixin, tables.LinkAction):
|
||||
name = "updateportforwardingrule"
|
||||
verbose_name = _("Update Rule")
|
||||
url = "horizon:project:routers:updateportforwardingrule"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
policy_rules = (("network", "update_portforwarding"),)
|
||||
|
||||
def get_link_url(self, datum=None):
|
||||
router_id = self.table.kwargs['router_id']
|
||||
portforwarding_id = datum['id']
|
||||
return reverse(self.url, args=(router_id, portforwarding_id,))
|
||||
|
||||
|
||||
class RemovePortForwardingRule(policy.PolicyTargetMixin, tables.DeleteAction):
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Delete Rule",
|
||||
u"Delete Rule",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Deleted Port Forwarding Rule",
|
||||
u"Deleted Port Forwarding Rules",
|
||||
count
|
||||
)
|
||||
|
||||
failure_url = 'horizon:project:routers:detail'
|
||||
policy_rules = (("network", "delete_portforwarding"),)
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
try:
|
||||
api.neutron.portforwarding_delete(
|
||||
request, portforwarding_id=obj_id)
|
||||
except Exception:
|
||||
msg = _('Failed to delete port forwarding rule %s') % obj_id
|
||||
LOG.info(msg)
|
||||
router_id = self.table.kwargs['router_id']
|
||||
redirect = reverse(self.failure_url,
|
||||
args=[router_id])
|
||||
exceptions.handle(request, msg, redirect=redirect)
|
||||
|
||||
|
||||
def _get_port_link_url(rule):
|
||||
port = rule['port']
|
||||
link = 'horizon:project:networks:ports:detail'
|
||||
return reverse(link, args=(port.id,))
|
||||
|
||||
|
||||
def _get_port_name_or_id(rule):
|
||||
port = rule['port']
|
||||
if port.name:
|
||||
return port.name
|
||||
return '(' + port.id[:8] + ')'
|
||||
|
||||
|
||||
class PortForwardingRulesTable(tables.DataTable):
|
||||
port = tables.Column(_get_port_name_or_id, verbose_name=_("Port"),
|
||||
link=_get_port_link_url)
|
||||
inside_addr = tables.Column("inside_addr",
|
||||
verbose_name=_("Private Address"))
|
||||
inside_port = tables.Column("inside_port", verbose_name=_("Private Port"))
|
||||
outside_port = tables.Column("outside_port", verbose_name=_("Public Port"))
|
||||
protocol = tables.Column("protocol", verbose_name=_("Protocol"))
|
||||
description = tables.Column("description", verbose_name=_("Description"))
|
||||
|
||||
def get_object_display(self, portforwarding):
|
||||
return portforwarding.id
|
||||
|
||||
class Meta(object):
|
||||
name = "portforwardings"
|
||||
verbose_name = _("Port Forwarding Rules")
|
||||
table_actions = (AddPortForwardingRule, RemovePortForwardingRule)
|
||||
row_actions = (UpdatePortForwardingRule, RemovePortForwardingRule, )
|
|
@ -0,0 +1,34 @@
|
|||
# Copyright (c) 2013-2015 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tabs
|
||||
from starlingx_dashboard import api as stx_api
|
||||
|
||||
|
||||
class OverviewTab(tabs.Tab):
|
||||
name = _("Overview")
|
||||
slug = "overview"
|
||||
template_name = "project/networks/portforwardings/_detail_overview.html"
|
||||
failure_url = 'horizon:project:routers:index'
|
||||
|
||||
def get_context_data(self, request):
|
||||
portforwarding_id = self.tab_group.kwargs['portforwarding_id']
|
||||
try:
|
||||
rule = stx_api.neutron.portforwarding_get(self.request,
|
||||
portforwarding_id)
|
||||
except Exception:
|
||||
redirect = reverse(self.failure_url)
|
||||
msg = _('Unable to retrieve port forwarding details.')
|
||||
exceptions.handle(request, msg, redirect=redirect)
|
||||
return {'portforwarding': rule}
|
||||
|
||||
|
||||
class PortForwardingDetailTabs(tabs.TabGroup):
|
||||
slug = "portforwarding_details"
|
||||
tabs = (OverviewTab,)
|
|
@ -0,0 +1,16 @@
|
|||
# Copyright (c) 2013-2015 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
from django.conf.urls import patterns
|
||||
from django.conf.urls import url
|
||||
|
||||
from openstack_dashboard.dashboards.project.routers.portforwardings \
|
||||
import views
|
||||
|
||||
PORTFORWARDINGS = r'^(?P<portforwarding_id>[^/]+)/%s$'
|
||||
|
||||
urlpatterns = patterns(
|
||||
'horizon.dashboards.project.networks.portforwardings.views',
|
||||
url(PORTFORWARDINGS % 'detail', views.DetailView.as_view(), name='detail'))
|
|
@ -0,0 +1,112 @@
|
|||
# Copyright (c) 2013-2016 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import tabs
|
||||
from horizon.utils import memoized
|
||||
|
||||
from openstack_dashboard import api
|
||||
from starlingx_dashboard import api as stx_api
|
||||
from starlingx_dashboard.dashboards.project.routers.portforwardings \
|
||||
import forms as project_forms
|
||||
from starlingx_dashboard.dashboards.project.routers.portforwardings \
|
||||
import tabs as project_tabs
|
||||
|
||||
|
||||
class AddPortForwardingRuleView(forms.ModalFormView):
|
||||
form_class = project_forms.AddPortForwardingRule
|
||||
template_name = 'project/routers/portforwardings/create.html'
|
||||
success_url = 'horizon:project:routers:detail'
|
||||
failure_url = 'horizon:project:routers:detail'
|
||||
page_title = _("Add Port Forwarding Rule")
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse(self.success_url,
|
||||
args=(self.kwargs['router_id'],))
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_router_object(self):
|
||||
try:
|
||||
router_id = self.kwargs["router_id"]
|
||||
return api.neutron.router_get(self.request, router_id)
|
||||
except Exception:
|
||||
redirect = reverse(self.failure_url, args=[router_id])
|
||||
msg = _("Unable to retrieve router.")
|
||||
exceptions.handle(self.request, msg, redirect=redirect)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = (super(AddPortForwardingRuleView, self).
|
||||
get_context_data(**kwargs))
|
||||
context['router'] = self.get_router_object()
|
||||
return context
|
||||
|
||||
def get_initial(self):
|
||||
router = self.get_router_object()
|
||||
return {"router_id": self.kwargs['router_id'],
|
||||
"router_name": router.name,
|
||||
"tenant_id": router.tenant_id}
|
||||
|
||||
|
||||
class UpdatePortForwardingRuleView(forms.ModalFormView):
|
||||
form_class = project_forms.UpdatePortForwardingRule
|
||||
template_name = 'project/routers/portforwardings/update.html'
|
||||
success_url = 'horizon:project:routers:detail'
|
||||
failure_url = 'horizon:project:routers:detail'
|
||||
page_title = _("Update Port Forwarding Rule")
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse(self.success_url,
|
||||
args=(self.kwargs['router_id'],))
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_router_object(self):
|
||||
try:
|
||||
router_id = self.kwargs["router_id"]
|
||||
return api.neutron.router_get(self.request, router_id)
|
||||
except Exception:
|
||||
redirect = reverse(self.failure_url, args=[router_id])
|
||||
msg = _("Unable to retrieve router.")
|
||||
exceptions.handle(self.request, msg, redirect=redirect)
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_portforwarding_object(self):
|
||||
try:
|
||||
router_id = self.kwargs["router_id"]
|
||||
portforwarding_id = self.kwargs["portforwarding_id"]
|
||||
return stx_api.neutron.portforwarding_get(self.request,
|
||||
portforwarding_id)
|
||||
except Exception:
|
||||
redirect = reverse(self.failure_url, args=[router_id])
|
||||
msg = _("Unable to retrieve port forwarding rule.")
|
||||
exceptions.handle(self.request, msg, redirect=redirect)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UpdatePortForwardingRuleView, self).get_context_data(
|
||||
**kwargs)
|
||||
context['router'] = self.get_router_object()
|
||||
context['portforwarding'] = self.get_portforwarding_object()
|
||||
return context
|
||||
|
||||
def get_initial(self):
|
||||
router = self.get_router_object()
|
||||
rule = self.get_portforwarding_object()
|
||||
return {"router_id": self.kwargs['router_id'],
|
||||
"router_name": router.name,
|
||||
"tenant_id": router.tenant_id,
|
||||
"portforwarding_id": self.kwargs['portforwarding_id'],
|
||||
"inside_addr": rule.inside_addr,
|
||||
"inside_port": rule.inside_port,
|
||||
"outside_port": rule.outside_port,
|
||||
"protocol": rule.protocol,
|
||||
"description": rule.description}
|
||||
|
||||
|
||||
class DetailView(tabs.TabView):
|
||||
tab_group_class = project_tabs.PortForwardingDetailTabs
|
||||
template_name = 'project/networks/portforwardings/detail.html'
|
|
@ -0,0 +1,18 @@
|
|||
from horizon import tabs
|
||||
|
||||
from starlingx_dashboard.api import base as stx_base
|
||||
from starlingx_dashboard.dashboards.project.routers.portforwardings\
|
||||
import tables
|
||||
|
||||
|
||||
class PortForwardingTab(tabs.TableTab):
|
||||
table_classes = (tables.PortForwardingRulesTable,)
|
||||
name = _("Port Forwarding")
|
||||
slug = "portforwardings"
|
||||
template_name = "horizon/common/_detail_table.html"
|
||||
|
||||
def get_portforwardings_data(self):
|
||||
return self.tab_group.kwargs['portforwardings']
|
||||
|
||||
def allowed(self, request):
|
||||
return stx_base.base.is_TiS_region(request)
|
|
@ -0,0 +1,27 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block form_id %}add_portforwarding_rule_form{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:project:routers:addportforwardingrule' router.id %}
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Add Port Forwarding Rule" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div class="left">
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="right">
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>
|
||||
{% trans "You can create a port forwarding rule from a public port to a private address and port." %}
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<a class="btn btn-default cancel" data-dismiss="modal">{% trans "Cancel" %}</a>
|
||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Add Rule" %}"/>
|
||||
{% endblock %}
|
|
@ -0,0 +1,27 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block form_id %}update_portforwarding_rule_form{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:project:routers:updateportforwardingrule' router.id portforwarding.id %}
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Update Port Forwarding Rule" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div class="left">
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="right">
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>
|
||||
{% trans "You can update the port forwarding rule details." %}
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<a class="btn btn-default cancel" data-dismiss="modal">{% trans "Cancel" %}</a>
|
||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Update Rule" %}"/>
|
||||
{% endblock %}
|
|
@ -0,0 +1,7 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Add Port Forwarding Rule" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include "project/routers/portforwardings/_create.html" %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,7 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Update Port Forwarding Rule" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include "project/routers/portforwardings/_update.html" %}
|
||||
{% endblock %}
|
Loading…
Reference in New Issue