186 lines
6.4 KiB
Python
186 lines
6.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright 2015 Mirantis, Inc.
|
|
#
|
|
# 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.
|
|
|
|
import errno
|
|
import os
|
|
import shutil
|
|
import tempfile
|
|
|
|
from solar.core.log import log
|
|
from solar.core.transports.ssh import SSHRunTransport
|
|
from solar.core.transports.ssh import SSHSyncTransport
|
|
from solar import errors
|
|
from solar import utils
|
|
|
|
|
|
tempfile.gettempdir()
|
|
|
|
SOLAR_TEMP_LOCAL_LOCATION = os.path.join(tempfile.tempdir, 'solar_local')
|
|
|
|
|
|
class BaseHandler(object):
|
|
|
|
def __init__(self, resources, handlers=None):
|
|
self.resources = resources
|
|
if handlers is None:
|
|
self.transport_sync = SSHSyncTransport()
|
|
self.transport_run = SSHRunTransport()
|
|
else:
|
|
self.transport_run = handlers['run']()
|
|
self.transport_sync = handlers['sync']()
|
|
self.transport_sync.bind_with(self.transport_run)
|
|
self.transport_run.bind_with(self.transport_sync)
|
|
|
|
def verify_run_result(self, cmd, result):
|
|
rc, out, err = result.return_code, result.stdout, result.stderr
|
|
log.debug('CMD %r RC %s OUT %s ERR %s', cmd, rc, out, err)
|
|
if not result.success:
|
|
message = 'CMD %r failed RC %s ERR %s' % (cmd, rc, err)
|
|
log.error(message)
|
|
raise errors.SolarError(result.output)
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, exc, value, traceback):
|
|
return
|
|
|
|
|
|
class TempFileHandler(BaseHandler):
|
|
|
|
def __init__(self, resources, handlers=None):
|
|
super(TempFileHandler, self).__init__(resources, handlers)
|
|
self.dst = None
|
|
|
|
def __enter__(self):
|
|
try:
|
|
self.dst = tempfile.mkdtemp(dir=SOLAR_TEMP_LOCAL_LOCATION)
|
|
except OSError as ex:
|
|
if ex.errno == errno.ENOENT:
|
|
# TODO: we have race condition there
|
|
# could be easily solved with lockfile
|
|
# but it's yet another extra dependency
|
|
# this solution is "hacky"
|
|
try:
|
|
os.makedirs(SOLAR_TEMP_LOCAL_LOCATION)
|
|
except OSError as ex:
|
|
if ex.errno == errno.EEXIST:
|
|
pass
|
|
else:
|
|
raise
|
|
self.dst = tempfile.mkdtemp(dir=SOLAR_TEMP_LOCAL_LOCATION)
|
|
else:
|
|
raise
|
|
self.dirs = {}
|
|
for resource in self.resources:
|
|
resource_dir = tempfile.mkdtemp(suffix=resource.name, dir=self.dst)
|
|
self.dirs[resource.name] = resource_dir
|
|
return self
|
|
|
|
def __exit__(self, type, value, traceback):
|
|
log.debug(self.dst)
|
|
return
|
|
shutil.rmtree(self.dst)
|
|
|
|
def _compile_action_file(self, resource, action):
|
|
dir_path = self.dirs[resource.name]
|
|
dest_file = tempfile.mkstemp(text=True, prefix=action, dir=dir_path)[1]
|
|
|
|
with open(dest_file, 'w') as f:
|
|
f.write(self._render_action(resource, action))
|
|
|
|
return dest_file
|
|
|
|
def _render_action(self, resource, action):
|
|
log.debug('Rendering %s %s', resource.name, action)
|
|
|
|
action_file = resource.actions[action]
|
|
log.debug('action file: %s', action_file)
|
|
args = self._make_args(resource)
|
|
|
|
return utils.render_template(
|
|
action_file,
|
|
str=str,
|
|
zip=zip,
|
|
**args)
|
|
|
|
def _render_dir(self, resource, _path):
|
|
rendered = []
|
|
args = self._make_args(resource)
|
|
for f in os.listdir(_path):
|
|
if f.endswith('.jinja'):
|
|
target_f = f[:-6]
|
|
full_target = os.path.join(_path, target_f)
|
|
full_src = os.path.join(_path, f)
|
|
with open(full_target, 'wb') as tmpl_f:
|
|
tpl = utils.render_template(
|
|
full_src,
|
|
str=str,
|
|
zip=zip,
|
|
**args)
|
|
tmpl_f.write(tpl)
|
|
log.debug("Rendered: %s", full_target)
|
|
os.remove(full_src)
|
|
rendered.append(full_target)
|
|
return rendered
|
|
|
|
def _copy_templates_and_scripts(self, resource, action):
|
|
# TODO: we might need to optimize it later, like provide list
|
|
# templates/scripts per action
|
|
log.debug("Adding templates for %s %s", resource.name, action)
|
|
trg_templates_dir = None
|
|
trg_scripts_dir = None
|
|
|
|
base_path = resource.db_obj.base_path
|
|
src_templates_dir = os.path.join(base_path, 'templates')
|
|
if os.path.exists(src_templates_dir):
|
|
trg_templates_dir = os.path.join(
|
|
self.dirs[resource.name], 'templates')
|
|
shutil.copytree(src_templates_dir, trg_templates_dir)
|
|
|
|
src_scripts_dir = os.path.join(base_path, 'scripts')
|
|
if os.path.exists(src_scripts_dir):
|
|
trg_scripts_dir = os.path.join(self.dirs[resource.name], 'scripts')
|
|
shutil.copytree(src_scripts_dir, trg_scripts_dir)
|
|
|
|
if trg_templates_dir:
|
|
self._render_dir(resource, trg_templates_dir)
|
|
|
|
return (trg_templates_dir, trg_scripts_dir)
|
|
|
|
def prepare_templates_and_scripts(self, resource, action, target_dir=None):
|
|
target_dir = target_dir or self.dirs[resource.name]
|
|
templates, scripts = self._copy_templates_and_scripts(resource, action)
|
|
if templates:
|
|
self.transport_sync.copy(resource, templates, target_dir)
|
|
if scripts:
|
|
self.transport_sync.copy(resource, scripts, target_dir)
|
|
|
|
def _make_args(self, resource):
|
|
args = {'resource_name': resource.name}
|
|
args['resource_dir'] = resource.db_obj.base_path
|
|
args['templates_dir'] = 'templates/'
|
|
args['scripts_dir'] = 'scripts/'
|
|
resource_args = resource.args
|
|
args['resource_args'] = resource_args
|
|
args.update(resource_args)
|
|
return args
|
|
|
|
|
|
class Empty(BaseHandler):
|
|
|
|
def action(self, resource, action):
|
|
pass
|