kolla-ansible/kolla_ansible/kolla_address.py
Mark Goddard fc2292b230 Use jinja2.pass_context instead of contextfilter
The contextfilter decorator was deprecated in jinja2 3.0.0, and has been
dropped in 3.1.0. This results in the following warning, and failed
attempts to use filters:

    [WARNING]: Skipping plugin (filters.py) as it seems to be invalid:
    module 'jinja2' has no attribute 'contextfilter'

This change switches to use the pass_context decorator. The minimum
version of Jinja2 is raised to 3 to ensure pass_context is present.

Change-Id: I649dd6211d3ae72b9539bc44652ef8cf5d579777
2022-03-24 16:29:26 +00:00

141 lines
5.7 KiB
Python

# -*- coding: utf-8 -*-
#
# Copyright 2019 Radosław Piliszek (yoctozepto)
#
# 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.
from jinja2.filters import pass_context
from jinja2.runtime import Undefined
from kolla_ansible.exception import FilterError
from kolla_ansible.helpers import _call_bool_filter
@pass_context
def kolla_address(context, network_name, hostname=None):
"""returns IP address on the requested network
The output is affected by '<network_name>_*' variables:
'<network_name>_interface' sets the interface to obtain address for.
'<network_name>_address_family' controls the address family (ipv4/ipv6).
:param context: Jinja2 Context
:param network_name: string denoting the name of the network to get IP
address for, e.g. 'api'
:param hostname: to override host which address is retrieved for
:returns: string with IP address
"""
# NOTE(yoctozepto): watch out as Jinja2 'context' behaves not exactly like
# the python 'dict' (but mimics it most of the time)
# for example it returns a special object of type 'Undefined' instead of
# 'None' or value specified as default for 'get' method
# 'HostVars' shares this behavior
if hostname is None:
hostname = context.get('inventory_hostname')
if isinstance(hostname, Undefined):
raise FilterError("'inventory_hostname' variable is unavailable")
hostvars = context.get('hostvars')
if isinstance(hostvars, Undefined):
raise FilterError("'hostvars' variable is unavailable")
host = hostvars.get(hostname)
if isinstance(host, Undefined):
raise FilterError("'{hostname}' not in 'hostvars'"
.format(hostname=hostname))
del hostvars # remove for clarity (no need for other hosts)
# NOTE(yoctozepto): variable "host" will *not* return Undefined
# same applies to all its children (act like plain dictionary)
interface_name = host.get(network_name + '_interface')
if interface_name is None:
raise FilterError("Interface name undefined "
"for network '{network_name}' "
"(set '{network_name}_interface')"
.format(network_name=network_name))
address_family = host.get(network_name + '_address_family')
if address_family is None:
raise FilterError("Address family undefined "
"for network '{network_name}' "
"(set '{network_name}_address_family')"
.format(network_name=network_name))
address_family = address_family.lower()
if address_family not in ['ipv4', 'ipv6']:
raise FilterError("Unknown address family '{address_family}' "
"for network '{network_name}'"
.format(address_family=address_family,
network_name=network_name))
ansible_interface_name = interface_name.replace('-', '_')
interface = host['ansible_facts'].get(ansible_interface_name)
if interface is None:
raise FilterError("Interface '{interface_name}' "
"not present "
"on host '{hostname}'"
.format(interface_name=interface_name,
hostname=hostname))
af_interface = interface.get(address_family)
if af_interface is None:
raise FilterError("Address family '{address_family}' undefined "
"on interface '{interface_name}' "
"for host: '{hostname}'"
.format(address_family=address_family,
interface_name=interface_name,
hostname=hostname))
if address_family == 'ipv4':
address = af_interface.get('address')
elif address_family == 'ipv6':
# ipv6 has no concept of a secondary address
# explicitly exclude the vip addresses
# to avoid excluding all /128
haproxy_enabled = host.get('enable_haproxy')
if haproxy_enabled is None:
raise FilterError("'enable_haproxy' variable is unavailable")
haproxy_enabled = _call_bool_filter(context, haproxy_enabled)
if haproxy_enabled:
vip_addresses = [
host.get('kolla_internal_vip_address'),
host.get('kolla_external_vip_address'),
]
else:
# no addresses are virtual (kolla-wise)
vip_addresses = []
global_ipv6_addresses = [x for x in af_interface if
x['scope'] == 'global' and
x['address'] not in vip_addresses]
if global_ipv6_addresses:
address = global_ipv6_addresses[0]['address']
else:
address = None
if address is None:
raise FilterError("{address_family} address missing "
"on interface '{interface_name}' "
"for host '{hostname}'"
.format(address_family=address_family,
interface_name=interface_name,
hostname=hostname))
return address