Evil Ham
ba77ea9edc
This makes it easier for new and experienced users to run cdist with higher verbosity levels, both to know that things are working as expected and to debug issues. Documentation has been modified accordingly and default behaviour is not changed.
229 lines
8.2 KiB
Python
229 lines
8.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
#
|
|
# 2011-2013 Steven Armstrong (steven-cdist at armstrong.cc)
|
|
# 2011-2013 Nico Schottelius (nico-cdist at schottelius.org)
|
|
#
|
|
# This file is part of cdist.
|
|
#
|
|
# cdist is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# cdist is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with cdist. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
#
|
|
|
|
import logging
|
|
import os
|
|
|
|
import cdist
|
|
from . import util
|
|
|
|
'''
|
|
common:
|
|
runs only locally, does not need remote
|
|
|
|
env:
|
|
PATH: prepend directory with type emulator symlinks == local.bin_path
|
|
__target_host: the target host we are working on
|
|
__target_hostname: the target hostname provided from __target_host
|
|
__target_fqdn: the target's fully qualified domain name provided from
|
|
__target_host
|
|
__global: full qualified path to the global
|
|
output dir == local.out_path
|
|
__cdist_manifest: full qualified path of the manifest == script
|
|
__cdist_type_base_path: full qualified path to the directory where
|
|
types are defined for use in type emulator
|
|
== local.type_path
|
|
__files: full qualified path to the files dir
|
|
__target_host_tags: comma spearated list of host tags
|
|
|
|
initial manifest is:
|
|
script: full qualified path to the initial manifest
|
|
|
|
env:
|
|
__manifest: path to .../conf/manifest/ == local.manifest_path
|
|
|
|
creates: new objects through type emulator
|
|
|
|
type manifeste is:
|
|
script: full qualified path to the type manifest
|
|
|
|
env:
|
|
__object: full qualified path to the object's dir
|
|
__object_id: the objects id
|
|
__object_fq: full qualified object id, iow: $type.name + / + object_id
|
|
__type: full qualified path to the type's dir
|
|
|
|
creates: new objects through type emulator
|
|
'''
|
|
|
|
|
|
class NoInitialManifestError(cdist.Error):
|
|
"""
|
|
Display missing initial manifest:
|
|
- Display path if user given
|
|
- try to resolve link if it is a link
|
|
- Omit path if default (is a linked path in temp directory without
|
|
much help)
|
|
"""
|
|
|
|
def __init__(self, manifest_path, user_supplied):
|
|
msg_header = "Initial manifest missing"
|
|
|
|
if user_supplied:
|
|
if os.path.islink(manifest_path):
|
|
self.message = "%s: %s -> %s" % (
|
|
msg_header, manifest_path,
|
|
os.path.realpath(manifest_path))
|
|
else:
|
|
self.message = "%s: %s" % (msg_header, manifest_path)
|
|
else:
|
|
self.message = "%s" % (msg_header)
|
|
|
|
def __str__(self):
|
|
return repr(self.message)
|
|
|
|
|
|
class Manifest(object):
|
|
"""Executes cdist manifests.
|
|
|
|
"""
|
|
|
|
ORDER_DEP_STATE_NAME = 'order_dep_state'
|
|
TYPEORDER_DEP_NAME = 'typeorder_dep'
|
|
|
|
def __init__(self, target_host, local, dry_run=False):
|
|
self.target_host = target_host
|
|
self.local = local
|
|
|
|
self._open_logger()
|
|
|
|
self.env = {
|
|
'PATH': "%s:%s" % (self.local.bin_path, os.environ['PATH']),
|
|
# for use in type emulator
|
|
'__cdist_type_base_path': self.local.type_path,
|
|
'__global': self.local.base_path,
|
|
'__target_host': self.target_host[0],
|
|
'__target_hostname': self.target_host[1],
|
|
'__target_fqdn': self.target_host[2],
|
|
'__files': self.local.files_path,
|
|
'__target_host_tags': self.local.target_host_tags,
|
|
'__cdist_log_level': util.log_level_env_var_val(self.log),
|
|
'__cdist_log_level_name': util.log_level_name_env_var_val(
|
|
self.log),
|
|
'__cdist_colored_log': str(cdist.log.ColorFormatter.USE_COLORS),
|
|
}
|
|
|
|
if dry_run:
|
|
self.env['__cdist_dry_run'] = '1'
|
|
|
|
def _open_logger(self):
|
|
self.log = logging.getLogger(self.target_host[0])
|
|
|
|
# logger is not pickable, so remove it when we pickle
|
|
def __getstate__(self):
|
|
state = self.__dict__.copy()
|
|
if 'log' in state:
|
|
del state['log']
|
|
return state
|
|
|
|
# recreate logger when we unpickle
|
|
def __setstate__(self, state):
|
|
self.__dict__.update(state)
|
|
self._open_logger()
|
|
|
|
def env_initial_manifest(self, initial_manifest):
|
|
env = os.environ.copy()
|
|
env.update(self.env)
|
|
env['__cdist_manifest'] = initial_manifest
|
|
env['__manifest'] = self.local.manifest_path
|
|
env['__explorer'] = self.local.global_explorer_out_path
|
|
|
|
return env
|
|
|
|
def run_initial_manifest(self, initial_manifest=None):
|
|
if not initial_manifest:
|
|
initial_manifest = self.local.initial_manifest
|
|
user_supplied = False
|
|
else:
|
|
user_supplied = True
|
|
|
|
if not os.path.isfile(initial_manifest):
|
|
raise NoInitialManifestError(initial_manifest, user_supplied)
|
|
|
|
message_prefix = "initialmanifest"
|
|
self.log.verbose("Running initial manifest " + initial_manifest)
|
|
which = "init"
|
|
if self.local.save_output_streams:
|
|
stderr_path = os.path.join(self.local.stderr_base_path, which)
|
|
stdout_path = os.path.join(self.local.stdout_base_path, which)
|
|
with open(stderr_path, 'ba+') as stderr, \
|
|
open(stdout_path, 'ba+') as stdout:
|
|
self.local.run_script(
|
|
initial_manifest,
|
|
env=self.env_initial_manifest(initial_manifest),
|
|
message_prefix=message_prefix,
|
|
stdout=stdout, stderr=stderr)
|
|
else:
|
|
self.local.run_script(
|
|
initial_manifest,
|
|
env=self.env_initial_manifest(initial_manifest),
|
|
message_prefix=message_prefix)
|
|
|
|
def env_type_manifest(self, cdist_object):
|
|
type_manifest = os.path.join(self.local.type_path,
|
|
cdist_object.cdist_type.manifest_path)
|
|
env = os.environ.copy()
|
|
env.update(self.env)
|
|
env.update({
|
|
'__cdist_manifest': type_manifest,
|
|
'__manifest': self.local.manifest_path,
|
|
'__object': cdist_object.absolute_path,
|
|
'__object_id': cdist_object.object_id,
|
|
'__object_name': cdist_object.name,
|
|
'__type': cdist_object.cdist_type.absolute_path,
|
|
})
|
|
|
|
return env
|
|
|
|
def run_type_manifest(self, cdist_object):
|
|
type_manifest = os.path.join(self.local.type_path,
|
|
cdist_object.cdist_type.manifest_path)
|
|
message_prefix = cdist_object.name
|
|
which = 'manifest'
|
|
if os.path.isfile(type_manifest):
|
|
self.log.verbose("Running type manifest %s for object %s",
|
|
type_manifest, cdist_object.name)
|
|
if self.local.save_output_streams:
|
|
stderr_path = os.path.join(cdist_object.stderr_path, which)
|
|
stdout_path = os.path.join(cdist_object.stdout_path, which)
|
|
with open(stderr_path, 'ba+') as stderr, \
|
|
open(stdout_path, 'ba+') as stdout:
|
|
self.local.run_script(
|
|
type_manifest,
|
|
env=self.env_type_manifest(cdist_object),
|
|
message_prefix=message_prefix,
|
|
stdout=stdout, stderr=stderr)
|
|
else:
|
|
self.local.run_script(
|
|
type_manifest,
|
|
env=self.env_type_manifest(cdist_object),
|
|
message_prefix=message_prefix)
|
|
|
|
def cleanup(self):
|
|
def _rm_file(fname):
|
|
try:
|
|
self.log.trace("[ORDER_DEP] Removing %s", fname)
|
|
os.remove(os.path.join(self.local.base_path, fname))
|
|
except FileNotFoundError:
|
|
pass
|
|
_rm_file(Manifest.ORDER_DEP_STATE_NAME)
|
|
_rm_file(Manifest.TYPEORDER_DEP_NAME)
|