tripleo-ansible/tripleo_ansible/ansible_plugins/filter/helpers.py

185 lines
6.8 KiB
Python

#!/usr/bin/env python
# Copyright 2019 Red Hat, Inc.
# All Rights Reserved.
#
# 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 json
from collections import OrderedDict
from operator import itemgetter
class FilterModule(object):
def filters(self):
return {
'singledict': self.singledict,
'subsort': self.subsort,
'needs_delete': self.needs_delete,
'haskey': self.haskey,
'list_of_keys': self.list_of_keys
}
def subsort(self, dict_to_sort, attribute, null_value=0):
"""Sort a hash from a sub-element.
This filter will return an dictionary ordered by the attribute
part of each item.
"""
for k, v in dict_to_sort.items():
if attribute not in v:
dict_to_sort[k][attribute] = null_value
data = {}
for d in dict_to_sort.items():
if d[1][attribute] not in data:
data[d[1][attribute]] = []
data[d[1][attribute]].append({d[0]: d[1]})
sorted_list = sorted(
data.items(),
key=lambda x: x[0]
)
ordered_dict = {}
for o, v in sorted_list:
ordered_dict[o] = v
return ordered_dict
def singledict(self, list_to_convert):
"""Generate a single dictionary from a list of dictionaries.
This filter will return a single dictionary from a list of
dictionaries.
"""
return_dict = {}
for i in list_to_convert:
return_dict.update(i)
return return_dict
def needs_delete(self, container_infos, config, config_id):
"""Returns a list of containers which need to be removed.
This filter will check which containers need to be removed for these
reasons: no config_data, updated config_data or container not
part of the global config.
"""
to_delete = []
to_skip = []
installed_containers = []
for c in container_infos:
c_name = c['Name']
installed_containers.append(c_name)
# Don't delete containers not managed by tripleo-ansible
if c['Config']['Labels'].get('managed_by') != 'tripleo_ansible':
to_skip += [c_name]
continue
# Only remove containers managed in this config_id
if c['Config']['Labels'].get('config_id') != config_id:
to_skip += [c_name]
continue
# Remove containers with no config_data
# e.g. broken config containers
if 'config_data' not in c['Config']['Labels']:
to_delete += [c_name]
continue
for c_name, config_data in config.items():
# don't try to remove a container which doesn't exist
if c_name not in installed_containers:
continue
# already tagged to be removed
if c_name in to_delete:
continue
if c_name in to_skip:
continue
# Remove containers managed by tripleo-ansible when config_data
# changed. Since we already cleaned the containers not in config,
# this check needs to be in that loop.
# e.g. new TRIPLEO_CONFIG_HASH during a minor update
try:
c_facts = [c['Config']['Labels']['config_data']
for c in container_infos if c_name == c['Name']]
except KeyError:
continue
# Build c_facts so it can be compared later with config_data;
# both will be json.dumps objects.
c_facts = json.dumps(
json.loads(c_facts[0]).get(c_name)
) if len(c_facts) == 1 else {}
# 0 was picked since it's the null_value for the subsort filter.
# When a container config doesn't provide the start_order, it'll be
# 0 by default, therefore it needs to be added in the config_data
# when comparing with the actual container_infos results.
if 'start_order' not in config_data:
config_data['start_order'] = 0
# TODO(emilien) double check the comparing here and see if
# types are accurate (string vs dict, etc)
if c_facts != json.dumps(config_data):
to_delete += [c_name]
continue
return to_delete
def haskey(self, batched_container_data, attribute, value=None,
reverse=False, any=False):
"""Return container data with a specific config key.
This filter will take a list of dictionaries (batched_container_data)
and will return the dictionnaries which have a certain key given
in parameter with 'attribute'.
If reverse is set to True, the returned list won't contain dictionaries
which have the attribute.
If any is set to True, the returned list will match any value in
the list of values for "value" parameter which has to be a list.
"""
return_list = []
for container in batched_container_data:
for k, v in json.loads(json.dumps(container)).items():
if attribute in v and not reverse:
if value is None:
return_list.append({k: v})
else:
if isinstance(value, list) and any:
if v[attribute] in value:
return_list.append({k: v})
elif any:
raise TypeError("value has to be a list if any is "
"set to True.")
else:
if v[attribute] == value:
return_list.append({k: v})
if attribute not in v and reverse:
return_list.append({k: v})
return return_list
def list_of_keys(self, keys_to_list):
"""Return a list of keys from a list of dictionaries.
This filter takes in input a list of dictionaries and for each of them
it will add the key to list_of_keys and returns it.
"""
list_of_keys = []
for i in keys_to_list:
for k, v in i.items():
list_of_keys.append(k)
return list_of_keys