validate-host: Options to require v4 and v6 routes

Make it possible for a site to demand that the validate-host role
finds IPv4 and/or IPv6 routes, making one or both explicitly
mandatory, instead of the default behavior of succeeding as long as
at least one is available. This allows a site to, for example,
discard nodes during a pre playbook if they lack IPv4 connectivity.

Change-Id: Icaa82212468a659a3756ed51cac442de33065b55
This commit is contained in:
Jeremy Stanley 2020-11-17 17:59:11 +00:00
parent 777230be59
commit 763916231f
3 changed files with 36 additions and 5 deletions

View File

@ -2,6 +2,20 @@ Log information about the build node
**Role Variables** **Role Variables**
.. zuul:rolevar:: zuul_site_ipv4_route_required
:default: false
If true, fail when no IPv4 route to ``zuul_site_traceroute_host`` is
available. When false (default) a missing IPv4 route is acceptable
so long as there is still a viable IPv6 route.
.. zuul:rolevar:: zuul_site_ipv6_route_required
:default: false
If true, fail when no IPv6 route to ``zuul_site_traceroute_host`` is
available. When false (default) a missing IPv6 route is acceptable
so long as there is still a viable IPv4 route.
.. zuul:rolevar:: zuul_site_traceroute_host .. zuul:rolevar:: zuul_site_traceroute_host
If defined, a host to run a traceroute against to verify build node If defined, a host to run a traceroute against to verify build node

View File

@ -42,12 +42,16 @@ def run_command(command):
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec=dict( argument_spec=dict(
ipv4_route_required=dict(required=False, type='bool'),
ipv6_route_required=dict(required=False, type='bool'),
image_manifest=dict(required=False, type='str'), image_manifest=dict(required=False, type='str'),
image_manifest_files=dict(required=False, type='list'), image_manifest_files=dict(required=False, type='list'),
traceroute_host=dict(required=False, type='str'), traceroute_host=dict(required=False, type='str'),
) )
) )
ipv4_route_required = module.params['ipv4_route_required']
ipv6_route_required = module.params['ipv6_route_required']
image_manifest = module.params['image_manifest'] image_manifest = module.params['image_manifest']
traceroute_host = module.params['traceroute_host'] traceroute_host = module.params['traceroute_host']
image_manifest_files = module.params['image_manifest_files'] image_manifest_files = module.params['image_manifest_files']
@ -64,29 +68,40 @@ def main():
'content': open(image_manifest, 'r').read(), 'content': open(image_manifest, 'r').read(),
}) })
if traceroute_host: if traceroute_host:
passed = False v6_passed = False
try: try:
ret['traceroute_v6'] = run_command( ret['traceroute_v6'] = run_command(
'traceroute6 -n {host}'.format(host=traceroute_host)) 'traceroute6 -n {host}'.format(host=traceroute_host))
passed = True v6_passed = True
except (subprocess.CalledProcessError, OSError) as e: except (subprocess.CalledProcessError, OSError) as e:
ret['traceroute_v6_exception'] = traceback.format_exc() ret['traceroute_v6_exception'] = traceback.format_exc()
ret['traceroute_v6_output'] = e.output ret['traceroute_v6_output'] = e.output
ret['traceroute_v6_return'] = e.returncode ret['traceroute_v6_return'] = e.returncode
pass pass
v4_passed = False
try: try:
ret['traceroute_v4'] = run_command( ret['traceroute_v4'] = run_command(
'traceroute -n {host}'.format(host=traceroute_host)) 'traceroute -n {host}'.format(host=traceroute_host))
passed = True v4_passed = True
except (subprocess.CalledProcessError, OSError) as e: except (subprocess.CalledProcessError, OSError) as e:
ret['traceroute_v4_exception'] = traceback.format_exc() ret['traceroute_v4_exception'] = traceback.format_exc()
ret['traceroute_v4_output'] = e.output ret['traceroute_v4_output'] = e.output
ret['traceroute_v4_return'] = e.returncode ret['traceroute_v4_return'] = e.returncode
pass pass
if v6_passed or v4_passed:
# By default, only require one IP family to have a working route,
# either version will suffice
passed = True
if ipv6_route_required and not v6_passed:
# Override the result if IPv6 is explicitly required
passed = False
if ipv4_route_required and not v4_passed:
# Override the result if IPv4 is explicitly required
passed = False
if not passed: if not passed:
module.fail_json( module.fail_json(
msg="No viable v4 or v6 route found to {traceroute_host}." msg="The required v4 or v6 route to {traceroute_host} was not"
" The build node is assumed to be invalid.".format( " found. The build node is assumed to be invalid.".format(
traceroute_host=traceroute_host), **ret) traceroute_host=traceroute_host), **ret)
for key, command in command_map.items(): for key, command in command_map.items():

View File

@ -29,6 +29,8 @@
block: block:
- name: Collect information about zuul worker - name: Collect information about zuul worker
zuul_debug_info: zuul_debug_info:
ipv4_route_required: "{{ zuul_site_ipv4_route_required|default(false) }}"
ipv6_route_required: "{{ zuul_site_ipv6_route_required|default(false) }}"
image_manifest: "{{ zuul_site_image_manifest|default(omit) }}" image_manifest: "{{ zuul_site_image_manifest|default(omit) }}"
image_manifest_files: "{{ zuul_site_image_manifest_files|default(omit) }}" image_manifest_files: "{{ zuul_site_image_manifest_files|default(omit) }}"
traceroute_host: "{{ zuul_site_traceroute_host|default(omit) }}" traceroute_host: "{{ zuul_site_traceroute_host|default(omit) }}"