Files
horizon/openstack_dashboard/dashboards/project/floating_ips/tables.py
Dmitriy Rabotyagov 2ec0177edc Use Python 3.12 for python3-django job
With migration from ubuntu jammy to noble, python3.11 is not available
anymore. This makes the job to fail on pre-install step.

So let's use Python 3.12 which is available out of the box on Noble
after switch.

This also bumps pylint version, as older one does not work
anymore with Python 3.12. New pylint brings quite some new
rules with it. Some were disabled, some were fixed within this
patch.

Change-Id: I4ba288966c582910e8a822d4531e29c9c005e48f
2024-12-01 21:48:52 +01:00

333 lines
12 KiB
Python

# Copyright 2012 Nebula, Inc.
# Copyright (c) 2012 X.commerce, a business unit of eBay Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
from django import shortcuts
from django.urls import reverse
from django.utils.http import urlencode
from django.utils.text import format_lazy
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext_lazy
from django.utils.translation import pgettext_lazy
from horizon import exceptions
from horizon import messages
from horizon import tables
from openstack_dashboard import api
from openstack_dashboard import policy
from openstack_dashboard.usage import quotas
from openstack_dashboard.utils import filters
LOG = logging.getLogger(__name__)
class AllocateIP(tables.LinkAction):
name = "allocate"
verbose_name = _("Allocate IP To Project")
classes = ("ajax-modal",)
icon = "link"
url = "horizon:project:floating_ips:allocate"
def single(self, data_table, request, *args):
return shortcuts.redirect('horizon:project:floating_ips:index')
def allowed(self, request, fip=None):
usages = quotas.tenant_quota_usages(request,
targets=('floatingip', ))
if 'floatingip' in usages and usages['floatingip']['available'] <= 0:
if "disabled" not in self.classes:
self.classes = list(self.classes) + ['disabled']
self.verbose_name = format_lazy(
'{verbose_name} {quota_exceeded}',
verbose_name=self.verbose_name,
quota_exceeded=_("(Quota exceeded)"))
else:
self.verbose_name = _("Allocate IP To Project")
classes = [c for c in self.classes if c != "disabled"]
self.classes = classes
policy_rules = (("network", "create_floatingip"),)
return policy.check(policy_rules, request)
class ReleaseIPs(tables.BatchAction):
name = "release"
action_type = "danger"
icon = "unlink"
help_text = _("Once a floating IP is released, there is"
" no guarantee the same IP can be allocated again.")
@staticmethod
def action_present(count):
return ngettext_lazy(
"Release Floating IP",
"Release Floating IPs",
count
)
@staticmethod
def action_past(count):
return ngettext_lazy(
"Released Floating IP",
"Released Floating IPs",
count
)
def allowed(self, request, fip=None):
policy_rules = (("network", "delete_floatingip"),)
port_forwarding_occurrence = 0
if fip:
pwds = fip.port_forwardings
port_forwarding_occurrence = len(pwds)
return port_forwarding_occurrence == 0 and policy.check(policy_rules,
request)
def action(self, request, obj_id):
api.neutron.tenant_floating_ip_release(request, obj_id)
class ReleaseIPsPortForwarding(ReleaseIPs):
name = "release_floating_ip_portforwarding_rule"
help_text = _(
"This floating IP has port forwarding rules configured to it."
" Therefore,"
" you will need to remove all of these rules before being able"
" to release it.")
def __init__(self, **kwargs):
attributes = {"title": "Release Floating IP with port forwarding rules",
"confirm-button-text": "Edit floating IP port"
" forwarding rules"}
super().__init__(attrs=attributes, **kwargs)
@staticmethod
def action_past(count):
return ngettext_lazy(
"Successfully redirected",
"Successfully redirected",
count
)
def allowed(self, request, fip=None):
policy_rules = (("network", "delete_floatingip_port_forwarding"),)
pwds = fip.port_forwardings
return (
len(pwds) > 0 and
policy.check(policy_rules, request) and
api.neutron.is_extension_floating_ip_port_forwarding_supported(
request)
)
def action(self, request, obj_id):
self.success_url = reverse(
'horizon:project:floating_ip_portforwardings:show') \
+ '?floating_ip_id=' \
+ str(obj_id)
class AssociateIP(tables.LinkAction):
name = "associate"
verbose_name = _("Associate")
url = "horizon:project:floating_ips:associate"
classes = ("ajax-modal",)
icon = "link"
def allowed(self, request, fip):
policy_rules = (("network", "update_floatingip"),)
pwds = fip.port_forwardings
return len(pwds) == 0 and not fip.port_id and policy.check(policy_rules,
request)
def get_link_url(self, datum):
base_url = reverse(self.url)
params = urlencode({"ip_id": self.table.get_object_id(datum)})
return "?".join([base_url, params])
class ListAllFloatingIpPortForwardingRules(tables.LinkAction):
name = "List floating_ip_portforwardings_rules"
verbose_name = _("List all floating IP port forwarding rules")
url = "horizon:project:floating_ip_portforwardings:index"
classes = ("btn-edit",)
icon = "link"
def exists_floating_ip_with_port_forwarding_rules_configurable(self,
request):
floating_ips = api.neutron.tenant_floating_ip_list(request)
for floating_ip in floating_ips:
if not floating_ip.port_id:
return True
return False
def allowed(self, request, fip):
policy_rules = (("network", "get_floatingip_port_forwarding"),)
return (self.exists_floating_ip_with_port_forwarding_rules_configurable(
request) and policy.check(policy_rules, request) and
api.neutron.is_extension_floating_ip_port_forwarding_supported(
request))
class ConfigureFloatingIpPortForwarding(tables.Action):
name = "configure_floating_ip_portforwarding_rules"
verbose_name = _("Configure floating IP port forwarding rules")
classes = ("btn-edit",)
icon = "link"
def allowed(self, request, fip):
policy_rules = (("network", "get_floatingip_port_forwarding"),)
return (
not fip.port_id and
policy.check(policy_rules, request) and
api.neutron.is_extension_floating_ip_port_forwarding_supported(
request)
)
def single(self, table, request, obj_id):
fip = {}
try:
fip = table.get_object_by_id(filters.get_int_or_uuid(obj_id))
except Exception as ex:
err_msg = 'Unable to find a floating IP.'
LOG.debug(err_msg, ex)
exceptions.handle(request,
_('Unable to find a floating IP.'))
return shortcuts.redirect(
reverse('horizon:project:floating_ip_portforwardings:show') +
'?floating_ip_id=' + str(fip.id))
class DisassociateIP(tables.Action):
name = "disassociate"
verbose_name = _("Disassociate")
classes = ("btn-disassociate",)
icon = "unlink"
action_type = "danger"
def allowed(self, request, fip):
policy_rules = (("network", "update_floatingip"),)
return fip.port_id and policy.check(policy_rules, request)
def single(self, table, request, obj_id):
try:
fip = table.get_object_by_id(filters.get_int_or_uuid(obj_id))
api.neutron.floating_ip_disassociate(request, fip.id)
LOG.info('Disassociating Floating IP "%s".', obj_id)
messages.success(request,
_('Successfully disassociated Floating IP: %s')
% fip.ip)
except Exception:
exceptions.handle(request,
_('Unable to disassociate floating IP.'))
return shortcuts.redirect('horizon:project:floating_ips:index')
def get_instance_info(fip):
if fip.instance_type == 'compute':
return (_("%(instance_name)s %(fixed_ip)s")
% {'instance_name': getattr(fip, "instance_name", ''),
'fixed_ip': fip.fixed_ip})
if fip.instance_type == 'loadbalancer':
return _("Load Balancer VIP %s") % fip.fixed_ip
return getattr(fip, 'fixed_ip', None)
def get_instance_link(datum):
if getattr(datum, 'instance_id'):
return reverse("horizon:project:instances:detail",
args=(datum.instance_id,))
return None
STATUS_DISPLAY_CHOICES = (
("active", pgettext_lazy("Current status of a Floating IP", "Active")),
("down", pgettext_lazy("Current status of a Floating IP", "Down")),
("error", pgettext_lazy("Current status of a Floating IP", "Error")),
)
FLOATING_IPS_FILTER_CHOICES = (
('floating_ip_address', _('Floating IP Address ='), True),
('network_id', _('Network ID ='), True),
('router_id', _('Router ID ='), True),
('port_id', _('Port ID ='), True),
('status', _('Status ='), True, _("e.g. ACTIVE / DOWN / ERROR")),
)
class FloatingIPsFilterAction(tables.FilterAction):
filter_type = "server"
filter_choices = FLOATING_IPS_FILTER_CHOICES
class FloatingIPsTable(tables.DataTable):
STATUS_CHOICES = (
("active", True),
("down", True),
("error", False)
)
ip = tables.Column("ip",
verbose_name=_("IP Address"),
attrs={'data-type': "ip"})
description = tables.Column("description",
verbose_name=_("Description"))
dns_name = tables.Column("dns_name",
verbose_name=_("DNS Name"))
dns_domain = tables.Column("dns_domain",
verbose_name=_("DNS Domain"))
fixed_ip = tables.Column(get_instance_info,
link=get_instance_link,
verbose_name=_("Mapped Fixed IP Address"))
pool = tables.Column("pool_name",
verbose_name=_("Pool"))
status = tables.Column("status",
verbose_name=_("Status"),
status=True,
status_choices=STATUS_CHOICES,
display_choices=STATUS_DISPLAY_CHOICES)
def __init__(self, request, data=None, needs_form_wrapper=None, **kwargs):
super().__init__(request, data=data,
needs_form_wrapper=needs_form_wrapper,
**kwargs)
dns_supported = api.neutron.is_extension_supported(
request,
"dns-integration")
if not dns_supported:
del self.columns["dns_name"]
del self.columns["dns_domain"]
def sanitize_id(self, obj_id):
return filters.get_int_or_uuid(obj_id)
def get_object_display(self, datum):
return datum.ip
class Meta(object):
name = "floating_ips"
verbose_name = _("Floating IPs")
table_actions = (
ListAllFloatingIpPortForwardingRules, AllocateIP, ReleaseIPs,
FloatingIPsFilterAction)
row_actions = (AssociateIP, DisassociateIP, ReleaseIPs,
ReleaseIPsPortForwarding,
ConfigureFloatingIpPortForwarding)