fc2292b230
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
141 lines
5.7 KiB
Python
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
|