# -*- 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 contextfilter from jinja2.runtime import Undefined from kolla_ansible.exception import FilterError from kolla_ansible.helpers import _call_bool_filter @contextfilter def kolla_address(context, network_name, hostname=None): """returns IP address on the requested network The output is affected by '_*' variables: '_interface' sets the interface to obtain address for. '_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