tripleo-ansible/tripleo_ansible/ansible_plugins/action/tripleo_nova_image_cache.py
Oliver Walsh 8dc6d50b16 Add role/playbook to manage nova image cache
Adds a tripleo-nova-image-cache role to manage the nova local image cache on
remote compute nodes.
For multi-site/stack deployments the image can be downloaded once and
distributed to the remaining nodes in the stack to minimise WAN traffic.

Also adds a playbook to run the role using the single/multi-stack inventory
and the overcloudrc environment variables.

Change-Id: Ib5aaa22f6cf307181d8f34cf89f9f24619b43004
Implements: blueprint tripleo-nova-cache-mgmt
2019-12-19 16:19:06 +00:00

179 lines
6.6 KiB
Python

#!/usr/bin/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.
from __future__ import absolute_import
__metaclass__ = type
import hashlib
import os
import uuid
from ansible.errors import AnsibleAction
from ansible.errors import AnsibleActionFail
from ansible.errors import AnsibleError
from ansible.plugins.action import ActionBase
from ansible.plugins.action import display
class ActionModule(ActionBase):
TRANSFERS_FILES = False
def run(self, tmp=None, task_vars=None):
if task_vars is None:
task_vars = dict()
result = super(ActionModule, self).run(tmp, task_vars)
del tmp # tmp no longer has any effect
scp_source = self._task.args.get('scp_source', None)
scp_continue = self._task.args.get('scp_continue_on_error', False)
state = self._task.args.get('state', 'present')
try:
# Ensure it's a valid uuid
image_id = str(uuid.UUID(self._task.args.get('id')))
except ValueError:
raise AnsibleError(
"Invalid image id: {}".format(
self._task.args.get('id')
)
)
cache_dir = task_vars.get(
'tripleo_nova_cache_dir',
'/var/lib/nova/instances/_base'
)
cache_fn = hashlib.sha1(image_id.encode('utf-8')).hexdigest()
cache_file = os.path.join(cache_dir, cache_fn)
cache_tmp = os.path.join(
cache_dir,
'ansible_tripleo_nova_cache_tmp_{}'.format(os.getpid())
)
tmp_file = os.path.join(cache_tmp, cache_fn)
container_cli = task_vars.get('container_cli', 'podman')
result.update({'actions': []})
try:
# Ensure target directory exists
command_args = {
'_raw_params':
(
"{} exec -u nova nova_compute /bin/bash -c "
"\"mkdir -p '{}'; chmod 755 '{}'\""
).format(container_cli, cache_dir, cache_dir),
'creates': cache_dir
}
command_task_vars = {'become': True}
command_result = self._execute_module(
'command',
module_args=command_args,
task_vars=command_task_vars
)
command_result['name'] = 'Ensure nova cache dir exists'
result['actions'].append(command_result)
cmd = self._connection._shell.exists(cache_file)
cache_file_exists_res = self._low_level_execute_command(
cmd,
sudoable=True
)
cache_file_exists = self._parse_returned_data(
cache_file_exists_res).get('rc', 0) == 0
result['actions'].append({
'name': 'Check if cache file exists',
'exists': cache_file_exists
})
new_module_args = self._task.args.copy()
new_module_args.pop('scp_source', None)
new_module_args['_cache_dir'] = cache_dir
new_module_args['_cache_file'] = cache_file
if state == 'present' and \
not cache_file_exists and \
scp_source is not None:
# Create tmp dir
command_args = {
'_raw_params':
(
"{} exec -u nova nova_compute /bin/bash -c "
"\"mkdir -p '{}'; chmod 755 '{}'\""
).format(container_cli, cache_tmp, cache_tmp),
}
command_task_vars = {'become': True}
command_result = self._execute_module(
'command',
module_args=command_args,
task_vars=command_task_vars
)
command_result['name'] = 'Create tmp dir'
result['actions'].append(command_result)
command_args = {
'_raw_params':
"{} exec -u nova nova_compute scp {}:'{}' '{}'".format(
container_cli,
scp_source,
cache_file,
cache_tmp
)
}
command_task_vars = {'become': True, 'ignore_errors': True}
command_result = self._execute_module(
'command',
module_args=command_args,
task_vars=command_task_vars)
command_result['name'] = 'Fetch image from {}'.format(
scp_source
)
result['actions'].append(command_result)
if command_result['rc'] == 0:
new_module_args['_prefetched_path'] = tmp_file
elif not scp_continue:
raise AnsibleActionFail(
'{} failed: {}'.format(
command_result['name'],
command_result['msg']
)
)
command_result = self._execute_module(
'tripleo_nova_image_cache',
module_args=new_module_args,
task_vars=task_vars
)
result['actions'] += command_result.pop('actions', [])
result.update(command_result)
except AnsibleAction as e:
result.update(e.result)
finally:
cmd = self._connection._shell.remove(cache_tmp, recurse=True)
tmp_rm_res = self._low_level_execute_command(cmd, sudoable=True)
tmp_rm_data = self._parse_returned_data(tmp_rm_res)
if tmp_rm_data.get('rc', 0) != 0:
display.warning(
'Error deleting remote temporary files '
' (rc: %s, stderr: %s})' % (
tmp_rm_res.get('rc'),
tmp_rm_res.get('stderr', 'No error string available.')
)
)
self._remove_tmp_path(self._connection._shell.tmpdir)
return result