TripleO Ansible project repository. Contains playbooks for use with TripleO OpenStack deployments.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

185 lines
6.8KB

  1. #!/usr/bin/env python
  2. # Copyright 2019 Red Hat, Inc.
  3. # All Rights Reserved.
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  6. # not use this file except in compliance with the License. You may obtain
  7. # a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  13. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  14. # License for the specific language governing permissions and limitations
  15. # under the License.
  16. import json
  17. from collections import OrderedDict
  18. from operator import itemgetter
  19. class FilterModule(object):
  20. def filters(self):
  21. return {
  22. 'singledict': self.singledict,
  23. 'subsort': self.subsort,
  24. 'needs_delete': self.needs_delete,
  25. 'haskey': self.haskey,
  26. 'list_of_keys': self.list_of_keys
  27. }
  28. def subsort(self, dict_to_sort, attribute, null_value=None):
  29. """Sort a hash from a sub-element.
  30. This filter will return an dictionary ordered by the attribute
  31. part of each item.
  32. """
  33. for k, v in dict_to_sort.items():
  34. if attribute not in v:
  35. dict_to_sort[k][attribute] = null_value
  36. data = {}
  37. for d in dict_to_sort.items():
  38. if d[1][attribute] not in data:
  39. data[d[1][attribute]] = []
  40. data[d[1][attribute]].append({d[0]: d[1]})
  41. sorted_list = sorted(
  42. data.items(),
  43. key=lambda x: x[0]
  44. )
  45. ordered_dict = {}
  46. for o, v in sorted_list:
  47. ordered_dict[o] = v
  48. return ordered_dict
  49. def singledict(self, list_to_convert):
  50. """Generate a single dictionary from a list of dictionaries.
  51. This filter will return a single dictionary from a list of
  52. dictionaries.
  53. """
  54. return_dict = {}
  55. for i in list_to_convert:
  56. return_dict.update(i)
  57. return return_dict
  58. def needs_delete(self, container_infos, config, config_id):
  59. """Returns a list of containers which need to be removed.
  60. This filter will check which containers need to be removed for these
  61. reasons: no config_data, updated config_data or container not
  62. part of the global config.
  63. """
  64. to_delete = []
  65. to_skip = []
  66. installed_containers = []
  67. for c in container_infos:
  68. c_name = c['Name']
  69. installed_containers.append(c_name)
  70. # Don't delete containers not managed by tripleo-ansible
  71. if c['Config']['Labels'].get('managed_by') != 'tripleo_ansible':
  72. to_skip += [c_name]
  73. continue
  74. # Only remove containers managed in this config_id
  75. if c['Config']['Labels'].get('config_id') != config_id:
  76. to_skip += [c_name]
  77. continue
  78. # Remove containers with no config_data
  79. # e.g. broken config containers
  80. if 'config_data' not in c['Config']['Labels']:
  81. to_delete += [c_name]
  82. continue
  83. for c_name, config_data in config.items():
  84. # don't try to remove a container which doesn't exist
  85. if c_name not in installed_containers:
  86. continue
  87. # already tagged to be removed
  88. if c_name in to_delete:
  89. continue
  90. if c_name in to_skip:
  91. continue
  92. # Remove containers managed by tripleo-ansible when config_data
  93. # changed. Since we already cleaned the containers not in config,
  94. # this check needs to be in that loop.
  95. # e.g. new TRIPLEO_CONFIG_HASH during a minor update
  96. try:
  97. c_facts = [c['Config']['Labels']['config_data']
  98. for c in container_infos if c_name == c['Name']]
  99. except KeyError:
  100. continue
  101. # Build c_facts so it can be compared later with config_data;
  102. # both will be json.dumps objects.
  103. c_facts = json.dumps(
  104. json.loads(c_facts[0]).get(c_name)
  105. ) if len(c_facts) == 1 else {}
  106. # 0 was picked since it's the null_value for the subsort filter.
  107. # When a container config doesn't provide the start_order, it'll be
  108. # 0 by default, therefore it needs to be added in the config_data
  109. # when comparing with the actual container_infos results.
  110. if 'start_order' not in config_data:
  111. config_data['start_order'] = 0
  112. # TODO(emilien) double check the comparing here and see if
  113. # types are accurate (string vs dict, etc)
  114. if c_facts != json.dumps(config_data):
  115. to_delete += [c_name]
  116. continue
  117. return to_delete
  118. def haskey(self, batched_container_data, attribute, value=None,
  119. reverse=False, any=False):
  120. """Return container data with a specific config key.
  121. This filter will take a list of dictionaries (batched_container_data)
  122. and will return the dictionnaries which have a certain key given
  123. in parameter with 'attribute'.
  124. If reverse is set to True, the returned list won't contain dictionaries
  125. which have the attribute.
  126. If any is set to True, the returned list will match any value in
  127. the list of values for "value" parameter which has to be a list.
  128. """
  129. return_list = []
  130. for container in batched_container_data:
  131. for k, v in json.loads(json.dumps(container)).items():
  132. if attribute in v and not reverse:
  133. if value is None:
  134. return_list.append({k: v})
  135. else:
  136. if isinstance(value, list) and any:
  137. if v[attribute] in value:
  138. return_list.append({k: v})
  139. elif any:
  140. raise TypeError("value has to be a list if any is "
  141. "set to True.")
  142. else:
  143. if v[attribute] == value:
  144. return_list.append({k: v})
  145. if attribute not in v and reverse:
  146. return_list.append({k: v})
  147. return return_list
  148. def list_of_keys(self, keys_to_list):
  149. """Return a list of keys from a list of dictionaries.
  150. This filter takes in input a list of dictionaries and for each of them
  151. it will add the key to list_of_keys and returns it.
  152. """
  153. list_of_keys = []
  154. for i in keys_to_list:
  155. for k, v in i.items():
  156. list_of_keys.append(k)
  157. return list_of_keys