Ansible deployment of the Kolla containers
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.
 
 
 
 

200 lines
7.0 KiB

  1. #!/usr/bin/env python
  2. # Licensed under the Apache License, Version 2.0 (the "License");
  3. # you may not use this file except in compliance with the License.
  4. # You may obtain a copy of the License at
  5. #
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software
  9. # distributed under the License is distributed on an "AS IS" BASIS,
  10. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. # See the License for the specific language governing permissions and
  12. # limitations under the License.
  13. import collections
  14. import fnmatch
  15. import json
  16. import logging
  17. import os
  18. import re
  19. import sys
  20. import jinja2
  21. import yaml
  22. from kolla_ansible.put_address_in_context import put_address_in_context
  23. PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
  24. NEWLINE_EOF_INCLUDE_PATTERNS = ['*.j2', '*.yml', '*.py', '*.sh']
  25. NEWLINE_EOF_EXCLUDE_PATTERNS = ['.tox', '.testrepository', '.git']
  26. # Render json file by using jinja2 template is OK
  27. JSON_J2_INCLUDE_PATTERNS = ['*.json.j2', '*.json']
  28. JSON_J2_EXCLUDE_PATTERNS = ['.tox', '.testrepository', '.git']
  29. YAML_INCLUDE_PATTERNS = ['*.yml']
  30. YAML_EXCLUDE_PATTERNS = ['.tox', '.testrepository', '.git',
  31. 'defaults', 'templates', 'vars']
  32. KOLLA_NETWORKS = [
  33. 'api',
  34. 'storage',
  35. 'swift_storage',
  36. 'swift_replication',
  37. 'migration',
  38. 'tunnel',
  39. 'octavia_network',
  40. 'bifrost_network',
  41. 'dns', # designate
  42. ]
  43. logging.basicConfig()
  44. LOG = logging.getLogger(__name__)
  45. def check_newline_eof():
  46. includes = r'|'.join([fnmatch.translate(x)
  47. for x in NEWLINE_EOF_INCLUDE_PATTERNS])
  48. excludes = r'|'.join([fnmatch.translate(x)
  49. for x in NEWLINE_EOF_EXCLUDE_PATTERNS])
  50. return_code = 0
  51. def has_newline_eof(path):
  52. with open(path, 'r') as f:
  53. data = f.read()
  54. if data and data[-1] != '\n':
  55. LOG.error('%s file error: no newline at end of file', path)
  56. return False
  57. return True
  58. for root, dirs, files in os.walk(PROJECT_ROOT):
  59. dirs[:] = [d for d in dirs if not re.match(excludes, d)]
  60. for f in files:
  61. if not re.match(excludes, f) and re.match(includes, f):
  62. if not has_newline_eof(os.path.join(root, f)):
  63. return_code = 1
  64. return return_code
  65. def check_json_j2():
  66. includes = r'|'.join([fnmatch.translate(x)
  67. for x in JSON_J2_INCLUDE_PATTERNS])
  68. excludes = r'|'.join([fnmatch.translate(x)
  69. for x in JSON_J2_EXCLUDE_PATTERNS])
  70. return_code = 0
  71. def bool_filter(value):
  72. return True
  73. def basename_filter(text):
  74. return text.split('\\')[-1]
  75. def kolla_address_filter_mock(network_name, hostname=None):
  76. # no validation is possible for the hostname
  77. if network_name not in KOLLA_NETWORKS:
  78. raise ValueError("{network_name} not in KOLLA_NETWORKS"
  79. .format(network_name=network_name))
  80. return "127.0.0.1"
  81. # Mock ansible hostvars variable, which is a nested dict
  82. def hostvars():
  83. return collections.defaultdict(hostvars)
  84. # Mock Ansible groups variable, which is a dict of lists.
  85. def groups():
  86. return collections.defaultdict(list)
  87. def validate_json_j2(root, filename):
  88. env = jinja2.Environment( # nosec: not used to render HTML
  89. loader=jinja2.FileSystemLoader(root))
  90. env.filters['bool'] = bool_filter
  91. env.filters['basename'] = basename_filter
  92. env.filters['kolla_address'] = kolla_address_filter_mock
  93. env.filters['put_address_in_context'] = \
  94. put_address_in_context
  95. template = env.get_template(filename)
  96. # Mock ansible variables.
  97. context = {
  98. 'hostvars': hostvars(),
  99. 'groups': groups(),
  100. 'storage_interface': 'storage_interface',
  101. 'inventory_hostname': 'hostname'
  102. }
  103. data = template.render(**context)
  104. json.loads(data)
  105. for root, dirs, files in os.walk(PROJECT_ROOT):
  106. dirs[:] = [d for d in dirs if not re.match(excludes, d)]
  107. for filename in files:
  108. if not re.match(excludes, filename) and \
  109. re.match(includes, filename):
  110. fullpath = os.path.join(root, filename)
  111. try:
  112. validate_json_j2(root, filename)
  113. except (ValueError, jinja2.exceptions.TemplateError):
  114. return_code = 1
  115. LOG.exception('%s file error', fullpath)
  116. return return_code
  117. def check_docker_become():
  118. """All tasks that use Docker should have 'become: true'."""
  119. includes = r'|'.join([fnmatch.translate(x)
  120. for x in YAML_INCLUDE_PATTERNS])
  121. excludes = r'|'.join([fnmatch.translate(x)
  122. for x in YAML_EXCLUDE_PATTERNS])
  123. docker_modules = ('kolla_docker', 'kolla_container_facts', 'kolla_toolbox')
  124. cmd_modules = ('command', 'shell')
  125. return_code = 0
  126. roles_path = os.path.join(PROJECT_ROOT, 'ansible', 'roles')
  127. for root, dirs, files in os.walk(roles_path):
  128. dirs[:] = [d for d in dirs if not re.match(excludes, d)]
  129. for filename in files:
  130. if not re.match(excludes, filename) and \
  131. re.match(includes, filename):
  132. fullpath = os.path.join(root, filename)
  133. with open(fullpath) as fp:
  134. tasks = yaml.safe_load(fp)
  135. tasks = tasks or []
  136. for task in tasks:
  137. for module in docker_modules:
  138. if module in task and not task.get('become'):
  139. return_code = 1
  140. LOG.error("Use of %s module without become in "
  141. "task %s in %s",
  142. module, task['name'], fullpath)
  143. for module in cmd_modules:
  144. docker_without_become = False
  145. if (module in task and not task.get('become')):
  146. if (isinstance(task[module], str) and
  147. (task[module]).startswith('docker')):
  148. docker_without_become = True
  149. if (isinstance(task[module], dict) and
  150. task[module]['cmd'].startswith('docker')):
  151. docker_without_become = True
  152. if docker_without_become:
  153. return_code = 1
  154. LOG.error("Use of docker in %s module without "
  155. "become in task %s in %s",
  156. module, task['name'], fullpath)
  157. return return_code
  158. def main():
  159. checks = (
  160. check_newline_eof,
  161. check_json_j2,
  162. check_docker_become,
  163. )
  164. return sum([check() for check in checks])
  165. if __name__ == "__main__":
  166. sys.exit(main())