8dc6d50b16
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
179 lines
6.6 KiB
Python
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
|