예제 #1
0
 def _load_yaml(self, path):
     """load a yaml file to a dict"""
     content = {}
     if not os.path.exists(path):
         raise YamlException('config path not found: {}'.format(path))
     try:
         content = self._yaml_load(path)
     except Exception as e:
         self.log.err(e)
         raise YamlException('invalid config: {}'.format(path))
     return content
예제 #2
0
 def _check_minversion(self, minversion):
     if not minversion:
         return
     try:
         cur = tuple([int(x) for x in VERSION.split('.')])
         cfg = tuple([int(x) for x in minversion.split('.')])
     except Exception:
         err = 'bad version: \"{}\" VS \"{}\"'.format(VERSION, minversion)
         raise YamlException(err)
     if cur < cfg:
         err = 'current dotdrop version is too old for that config file.'
         err += ' Please update.'
         raise YamlException(err)
예제 #3
0
 def _import_sub(self,
                 path,
                 key,
                 mandatory=False,
                 patch_func=None,
                 fatal_not_found=True):
     """
     import the block "key" from "path"
     patch_func is applied to each element if defined
     """
     if self.debug:
         self.log.dbg('import \"{}\" from \"{}\"'.format(key, path))
         self.log.dbg('ignore non existing: \"{}\"'.format(fatal_not_found))
     extdict = self._load_yaml(path, fatal_not_found=fatal_not_found)
     if extdict is None and not fatal_not_found:
         return {}
     new = self._get_entry(extdict, key, mandatory=mandatory)
     if patch_func:
         if self.debug:
             self.log.dbg('calling patch: {}'.format(patch_func))
         new = patch_func(new)
     if not new and mandatory:
         err = 'no \"{}\" imported from \"{}\"'.format(key, path)
         self.log.warn(err)
         raise YamlException(err)
     if self.debug:
         self.log.dbg('imported \"{}\": {}'.format(key, new))
     return new
예제 #4
0
    def save(self):
        """save this instance and return True if saved"""
        if not self._dirty:
            return False

        content = self._prepare_to_save(self._yaml_dict)

        if self._dirty_deprecated:
            # add minversion
            settings = content[self.key_settings]
            settings[self.key_settings_minversion] = VERSION

        # save to file
        if self._debug:
            self._dbg('saving to {}'.format(self._path))
        try:
            with open(self._path, 'w') as f:
                self._yaml_dump(content, f)
        except Exception as e:
            self._log.err(e)
            raise YamlException('error saving config: {}'.format(self._path))

        if self._dirty_deprecated:
            warn = 'your config contained deprecated entries'
            warn += ' and was updated'
            self._log.warn(warn)

        self._dirty = False
        self.cfg_updated = False
        return True
예제 #5
0
    def __init__(self, args=None):
        """constructor
        @args: argument dictionary (if None use sys)
        """
        self.args = {}
        if not args:
            self.args = docopt(USAGE, version=VERSION)
        if args:
            self.args = args.copy()
        self.log = Logger()
        self.debug = self.args['--verbose'] or ENV_DEBUG in os.environ
        self.dry = self.args['--dry']
        if ENV_NODEBUG in os.environ:
            # force disabling debugs
            self.debug = False
        self.profile = self.args['--profile']
        self.confpath = self._get_config_path()
        if not self.confpath:
            raise YamlException('no config file found')
        if self.debug:
            self.log.dbg('version: {}'.format(VERSION))
            self.log.dbg('command: {}'.format(' '.join(sys.argv)))
            self.log.dbg('config file: {}'.format(self.confpath))

        self._read_config()
        self._apply_args()
        self._fill_attr()
        if ENV_NOBANNER not in os.environ \
           and self.banner \
           and not self.args['--no-banner']:
            self._header()
        self._debug_attr()
        # start monitoring for bad attribute
        self._set_attr_err = True
예제 #6
0
    def save(self):
        """save this instance and return True if saved"""
        if not self.dirty:
            return False

        content = self._clear_none(self.dump())

        # make sure we have the base entries
        if self.key_settings not in content:
            content[self.key_settings] = None
        if self.key_dotfiles not in content:
            content[self.key_dotfiles] = None
        if self.key_profiles not in content:
            content[self.key_profiles] = None

        # save to file
        if self.debug:
            self.log.dbg('saving to {}'.format(self.path))
        try:
            self._yaml_dump(content, self.path)
        except Exception as e:
            self.log.err(e)
            raise YamlException('error saving config: {}'.format(self.path))

        self.dirty = False
        return True
예제 #7
0
 def _load_yaml(self, path, fatal_not_found=True):
     """load a yaml file to a dict"""
     content = {}
     if not os.path.exists(path):
         err = 'config path not found: {}'.format(path)
         if fatal_not_found:
             raise YamlException(err)
         else:
             self.log.warn(err)
             return None
     try:
         content = self._yaml_load(path)
     except Exception as e:
         self.log.err(e)
         raise YamlException('invalid config: {}'.format(path))
     return content
예제 #8
0
 def _load_yaml(self, path):
     """load a yaml file to a dict"""
     content = {}
     try:
         content = self._yaml_load(path)
     except Exception as e:
         self.log.err(e)
         raise YamlException('invalid config: {}'.format(path))
     return content
예제 #9
0
 def _get_entry(self, dic, key, mandatory=True):
     """return entry from yaml dictionary"""
     if key not in dic:
         if mandatory:
             raise YamlException('invalid config: no {} found'.format(key))
         dic[key] = {}
         return dic[key]
     if mandatory and not dic[key]:
         # ensure is not none
         dic[key] = {}
     return dic[key]
예제 #10
0
    def _rec_resolve_profile_include(self, profile):
        """
        recursively resolve include of other profiles's:
        * dotfiles
        * actions
        """
        this_profile = self.profiles[profile]

        # include
        dotfiles = this_profile.get(self.key_profile_dotfiles, [])
        actions = this_profile.get(self.key_profile_actions, [])
        includes = this_profile.get(self.key_profile_include, None)
        if not includes:
            # nothing to include
            return dotfiles, actions
        if self.debug:
            self.log.dbg('{} includes: {}'.format(profile, ','.join(includes)))
            self.log.dbg('{} dotfiles before include: {}'.format(
                profile, dotfiles))
            self.log.dbg('{} actions before include: {}'.format(
                profile, actions))

        seen = []
        for i in uniq_list(includes):
            # ensure no include loop occurs
            if i in seen:
                raise YamlException('\"include loop\"')
            seen.append(i)
            # included profile even exists
            if i not in self.profiles.keys():
                self.log.warn('include unknown profile: {}'.format(i))
                continue
            # recursive resolve
            o_dfs, o_actions = self._rec_resolve_profile_include(i)
            # merge dotfile keys
            dotfiles.extend(o_dfs)
            this_profile[self.key_profile_dotfiles] = uniq_list(dotfiles)
            # merge actions keys
            actions.extend(o_actions)
            this_profile[self.key_profile_actions] = uniq_list(actions)

        dotfiles = this_profile.get(self.key_profile_dotfiles, [])
        actions = this_profile.get(self.key_profile_actions, [])
        if self.debug:
            self.log.dbg('{} dotfiles after include: {}'.format(
                profile, dotfiles))
            self.log.dbg('{} actions after include: {}'.format(
                profile, actions))

        # since dotfiles and actions are resolved here
        # and variables have been already done at the beginning
        # of the parsing, we can clear these include
        self.profiles[profile][self.key_profile_include] = None
        return dotfiles, actions
예제 #11
0
    def __init__(self, path, profile=None, debug=False):
        """
        config parser
        @path: config file path
        @profile: the selected profile
        @debug: debug flag
        """
        self.path = os.path.abspath(path)
        self.profile = profile
        self.debug = debug
        self.log = Logger()
        # config needs to be written
        self.dirty = False
        # indicates the config has been updated
        self.dirty_deprecated = False

        if not os.path.exists(path):
            err = 'invalid config path: \"{}\"'.format(path)
            if self.debug:
                self.log.dbg(err)
            raise YamlException(err)

        self.yaml_dict = self._load_yaml(self.path)
        # live patch deprecated entries
        self._fix_deprecated(self.yaml_dict)
        # parse to self variables
        self._parse_main_yaml(self.yaml_dict)
        if self.debug:
            self.log.dbg('before normalization: {}'.format(self.yaml_dict))

        # resolve variables
        self.variables, self.prokeys = self._merge_variables()

        # apply variables
        self._apply_variables()

        # process imported variables (import_variables)
        self._import_variables()
        # process imported actions (import_actions)
        self._import_actions()
        # process imported profile dotfiles (import)
        self._import_profiles_dotfiles()
        # process imported configs (import_configs)
        self._import_configs()

        # process profile include
        self._resolve_profile_includes()
        # process profile ALL
        self._resolve_profile_all()
        # patch dotfiles paths
        self._resolve_dotfile_paths()

        if self.debug:
            self.log.dbg('after normalization: {}'.format(self.yaml_dict))
예제 #12
0
 def _shell_exec_dvars(self, keys, variables):
     """shell execute dynvariables"""
     for k in list(keys):
         ret, out = shell(variables[k], debug=self.debug)
         if not ret:
             err = 'var \"{}: {}\" failed: {}'.format(k, variables[k], out)
             self.log.err(err)
             raise YamlException(err)
         if self.debug:
             self.log.dbg('\"{}\": {} -> {}'.format(k, variables[k], out))
         variables[k] = out
예제 #13
0
파일: options.py 프로젝트: davla/dotdrop
    def __init__(self, args=None):
        """constructor
        @args: argument dictionary (if None use sys)
        """
        # attributes gotten from self.conf.get_settings()
        self.banner = None
        self.showdiff = None
        self.default_actions = []
        self.instignore = None
        self.force_chmod = None
        self.cmpignore = None
        self.impignore = None
        self.upignore = None
        self.link_on_import = None
        self.chmod_on_import = None
        self.check_version = None
        self.clear_workdir = None
        self.key_prefix = None
        self.key_separator = None

        # args parsing
        self.args = {}
        if not args:
            self.args = docopt(USAGE, version=VERSION)
        if args:
            self.args = args.copy()
        self.debug = self.args['--verbose'] or ENV_DEBUG in os.environ
        self.log = Logger(debug=self.debug)
        self.dry = self.args['--dry']
        if ENV_NODEBUG in os.environ:
            # force disabling debugs
            self.debug = False
        self.profile = self.args['--profile']
        self.confpath = self._get_config_path()
        if not self.confpath:
            raise YamlException('no config file found')
        self.log.dbg('#################################################')
        self.log.dbg('#################### DOTDROP ####################')
        self.log.dbg('#################################################')
        self.log.dbg('version: {}'.format(VERSION))
        self.log.dbg('command: {}'.format(' '.join(sys.argv)))
        self.log.dbg('config file: {}'.format(self.confpath))

        self._read_config()
        self._apply_args()
        self._fill_attr()
        if ENV_NOBANNER not in os.environ \
           and self.banner \
           and not self.args['--no-banner']:
            self._header()
        self._debug_attr()
        # start monitoring for bad attribute
        self._set_attr_err = True
예제 #14
0
    def _parse_blk_dotfiles(self, dic):
        """parse the "dotfiles" block"""
        dotfiles = self._get_entry(dic, self.key_dotfiles).copy()
        keys = dotfiles.keys()
        if len(keys) != len(list(set(keys))):
            dups = [x for x in keys if x not in list(set(keys))]
            err = 'duplicate dotfile keys found: {}'.format(dups)
            raise YamlException(err)

        dotfiles = self._norm_dotfiles(dotfiles)
        if self._debug:
            self._debug_dict('dotfiles block', dotfiles)
        return dotfiles
예제 #15
0
 def _shell_exec_dvars(self, dic, keys=[]):
     """shell execute dynvariables in-place"""
     if not keys:
         keys = dic.keys()
     for k in keys:
         v = dic[k]
         ret, out = shell(v, debug=self._debug)
         if not ret:
             err = 'var \"{}: {}\" failed: {}'.format(k, v, out)
             self._log.err(err)
             raise YamlException(err)
         if self._debug:
             self._dbg('{}: `{}` -> {}'.format(k, v, out))
         dic[k] = out
예제 #16
0
 def _glob_paths(self, paths):
     """glob a list of paths"""
     if not isinstance(paths, list):
         paths = [paths]
     res = []
     for p in paths:
         if not self._is_glob(p):
             res.append(p)
             continue
         p = os.path.expanduser(p)
         new = glob.glob(p)
         if not new:
             raise YamlException('bad path: {}'.format(p))
         res.extend(glob.glob(p))
     return res
예제 #17
0
 def _load_yaml(self, path):
     """load a yaml file to a dict"""
     content = {}
     if self._debug:
         self._dbg('----------start:{}----------'.format(path))
         cfg = '\n'
         with open(path, 'r') as f:
             for line in f:
                 cfg += line
         self._dbg(cfg.rstrip())
         self._dbg('----------end:{}----------'.format(path))
     try:
         content = self._yaml_load(path)
     except Exception as e:
         self._log.err(e)
         raise YamlException('invalid config: {}'.format(path))
     return content
예제 #18
0
 def _import_sub(self, path, key, mandatory=False, patch_func=None):
     """
     import the block "key" from "path"
     patch_func is applied to each element if defined
     """
     if self.debug:
         self.log.dbg('import \"{}\" from \"{}\"'.format(key, path))
     extdict = self._load_yaml(path)
     new = self._get_entry(extdict, key, mandatory=mandatory)
     if patch_func:
         new = patch_func(new)
     if not new and mandatory:
         err = 'no \"{}\" imported from \"{}\"'.format(key, path)
         self.log.warn(err)
         raise YamlException(err)
     if self.debug:
         self.log.dbg('new \"{}\": {}'.format(key, new))
     return new
예제 #19
0
    def _get_included_variables(self, profile, seen):
        """return included variables"""
        variables = {}
        if not profile or profile not in self.profiles.keys():
            return variables

        # profile entry
        pentry = self.profiles.get(profile)

        # inherite profile variables
        for inherited_profile in pentry.get(self.key_profile_include, []):
            if inherited_profile == profile or inherited_profile in seen:
                raise YamlException('\"include\" loop')
            seen.append(inherited_profile)
            new = self._get_included_variables(inherited_profile, seen)
            if self.debug:
                msg = 'included vars from {}: {}'
                self.log.dbg(msg.format(inherited_profile, new))
            variables.update(new)

        cur = pentry.get(self.key_profile_variables, {})
        return self._merge_dict(cur, variables)
예제 #20
0
    def __get_profile_included_item(self, profile, keyitem, seen):
        """recursively get included <keyitem> from profile"""
        items = {}
        if not profile or profile not in self.profiles.keys():
            return items

        # considered profile entry
        pentry = self.profiles.get(profile)

        # recursively get <keyitem> from inherited profile
        for inherited_profile in pentry.get(self.key_profile_include, []):
            if inherited_profile == profile or inherited_profile in seen:
                raise YamlException('\"include\" loop')
            seen.append(inherited_profile)
            new = self.__get_profile_included_item(inherited_profile, keyitem,
                                                   seen)
            if self._debug:
                msg = 'included {} from {}: {}'
                self._dbg(msg.format(keyitem, inherited_profile, new))
            items.update(new)

        cur = pentry.get(keyitem, {})
        return self._merge_dict(cur, items)
예제 #21
0
    def save(self):
        """save this instance and return True if saved"""
        if not self.dirty:
            return False

        content = self._clear_none(self.dump())

        # make sure we have the base entries
        if self.key_settings not in content:
            content[self.key_settings] = None
        if self.key_dotfiles not in content:
            content[self.key_dotfiles] = None
        if self.key_profiles not in content:
            content[self.key_profiles] = None

        if self.dirty_deprecated:
            # add minversion
            settings = content[self.key_settings]
            settings[self.key_settings_minversion] = VERSION

        # save to file
        if self.debug:
            self.log.dbg('saving to {}'.format(self.path))
        try:
            self._yaml_dump(content, self.path)
        except Exception as e:
            self.log.err(e)
            raise YamlException('error saving config: {}'.format(self.path))

        if self.dirty_deprecated:
            warn = 'your config contained deprecated entries'
            warn += ' and was updated'
            self.log.warn(warn)

        self.dirty = False
        self.cfg_updated = False
        return True
예제 #22
0
    def _import_config(self, path):
        """import config from path"""
        path, fatal_not_found = self._norm_extended_import_path(path)
        if self.debug:
            self.log.dbg('import config from {}'.format(path))
        if not os.path.exists(path):
            err = 'config path not found: {}'.format(path)
            if fatal_not_found:
                raise YamlException(err)
            else:
                self.log.warn(err)
                return
        sub = CfgYaml(path, profile=self.profile, debug=self.debug)

        # settings are ignored from external file
        # except for filter_file and func_file
        self.settings[Settings.key_func_file] += [
            self._norm_path(func_file)
            for func_file in sub.settings[Settings.key_func_file]
        ]
        self.settings[Settings.key_filter_file] += [
            self._norm_path(func_file)
            for func_file in sub.settings[Settings.key_filter_file]
        ]

        # merge top entries
        self.dotfiles = self._merge_dict(self.dotfiles, sub.dotfiles)
        self.profiles = self._merge_dict(self.profiles, sub.profiles)
        self.actions = self._merge_dict(self.actions, sub.actions)
        self.trans_r = self._merge_dict(self.trans_r, sub.trans_r)
        self.trans_w = self._merge_dict(self.trans_w, sub.trans_w)
        self._clear_profile_vars(sub.variables)

        if self.debug:
            self.log.dbg('add import_configs var: {}'.format(sub.variables))
        self.variables = self._merge_dict(sub.variables, self.variables)
예제 #23
0
    def _rec_resolve_profile_include(self, profile):
        """
        recursively resolve include of other profiles's:
        * dotfiles
        * actions
        * variables
        * dynvariables
        variables/dynvariables are directly merged with the
        global variables (self.variables) if these are
        included in the selected profile
        returns dotfiles, actions, variables, dynvariables
        """
        this_profile = self.profiles[profile]

        # considered profile content
        dotfiles = this_profile.get(self.key_profile_dotfiles, []) or []
        actions = this_profile.get(self.key_profile_actions, []) or []
        includes = this_profile.get(self.key_profile_include, []) or []
        pvars = this_profile.get(self.key_profile_variables, {}) or {}
        pdvars = this_profile.get(self.key_profile_dvariables, {}) or {}
        if not includes:
            # nothing to include
            return dotfiles, actions, pvars, pdvars

        if self.debug:
            self.log.dbg('{} includes {}'.format(profile, ','.join(includes)))
            self.log.dbg('{} dotfiles before include: {}'.format(
                profile, dotfiles))
            self.log.dbg('{} actions before include: {}'.format(
                profile, actions))
            self.log.dbg('{} variables before include: {}'.format(
                profile, pvars))
            self.log.dbg('{} dynvariables before include: {}'.format(
                profile, pdvars))

        seen = []
        for i in uniq_list(includes):
            if self.debug:
                self.log.dbg('resolving includes "{}" <- "{}"'.format(
                    profile, i))

            # ensure no include loop occurs
            if i in seen:
                raise YamlException('\"include loop\"')
            seen.append(i)
            # included profile even exists
            if i not in self.profiles.keys():
                self.log.warn('include unknown profile: {}'.format(i))
                continue

            # recursive resolve
            if self.debug:
                self.log.dbg(
                    'recursively resolving includes for profile "{}"'.format(
                        i))
            o_dfs, o_actions, o_v, o_dv = self._rec_resolve_profile_include(i)

            # merge dotfile keys
            if self.debug:
                self.log.dbg('Merging dotfiles {} <- {}: {} <- {}'.format(
                    profile, i, dotfiles, o_dfs))
            dotfiles.extend(o_dfs)
            this_profile[self.key_profile_dotfiles] = uniq_list(dotfiles)

            # merge actions keys
            if self.debug:
                self.log.dbg('Merging actions {} <- {}: {} <- {}'.format(
                    profile, i, actions, o_actions))
            actions.extend(o_actions)
            this_profile[self.key_profile_actions] = uniq_list(actions)

            # merge variables
            if self.debug:
                self.log.dbg('Merging variables {} <- {}: {} <- {}'.format(
                    profile, i, dict(pvars), dict(o_v)))
            pvars = self._merge_dict(o_v, pvars)
            this_profile[self.key_profile_variables] = pvars

            # merge dynvariables
            if self.debug:
                self.log.dbg(
                    'Merging dynamic variables {} <- {}: {} <- {}'.format(
                        profile, i, dict(pdvars), dict(o_dv)))
            pdvars = self._merge_dict(o_dv, pdvars)
            this_profile[self.key_profile_dvariables] = pdvars

        dotfiles = this_profile.get(self.key_profile_dotfiles, [])
        actions = this_profile.get(self.key_profile_actions, [])
        pvars = this_profile.get(self.key_profile_variables, {}) or {}
        pdvars = this_profile.get(self.key_profile_dvariables, {}) or {}

        if self.debug:
            self.log.dbg('{} dotfiles after include: {}'.format(
                profile, dotfiles))
            self.log.dbg('{} actions after include: {}'.format(
                profile, actions))
            self.log.dbg('{} variables after include: {}'.format(
                profile, pvars))
            self.log.dbg('{} dynvariables after include: {}'.format(
                profile, pdvars))

        if profile == self.profile:
            # Only for the selected profile, we execute dynamic variables and
            # we merge variables/dynvariables into the global variables
            self._shell_exec_dvars(pdvars.keys(), pdvars)
            self.variables = self._merge_dict(pvars, self.variables)
            self.variables = self._merge_dict(pdvars, self.variables)

        # since included items are resolved here
        # we can clear these include
        self.profiles[profile][self.key_profile_include] = None
        return dotfiles, actions, pvars, pdvars
예제 #24
0
 def _handle_non_existing_path(self, path, fatal_not_found=True):
     """Raise an exception or log a warning to handle non-existing paths."""
     error = 'bad path {}'.format(path)
     if fatal_not_found:
         raise YamlException(error)
     self._log.warn(error)
예제 #25
0
    def _rec_resolve_profile_include(self, profile):
        """
        recursively resolve include of other profiles's:
        * dotfiles
        * actions
        returns dotfiles, actions
        """
        this_profile = self.profiles[profile]

        # considered profile content
        dotfiles = this_profile.get(self.key_profile_dotfiles, []) or []
        actions = this_profile.get(self.key_profile_actions, []) or []
        includes = this_profile.get(self.key_profile_include, []) or []
        if not includes:
            # nothing to include
            return dotfiles, actions

        if self._debug:
            self._dbg('{} includes {}'.format(profile, ','.join(includes)))
            self._dbg('{} dotfiles before include: {}'.format(
                profile, dotfiles))
            self._dbg('{} actions before include: {}'.format(profile, actions))

        seen = []
        for i in uniq_list(includes):
            if self._debug:
                self._dbg('resolving includes "{}" <- "{}"'.format(profile, i))

            # ensure no include loop occurs
            if i in seen:
                raise YamlException('\"include loop\"')
            seen.append(i)
            # included profile even exists
            if i not in self.profiles.keys():
                self._log.warn('include unknown profile: {}'.format(i))
                continue

            # recursive resolve
            if self._debug:
                self._dbg(
                    'recursively resolving includes for profile "{}"'.format(
                        i))
            o_dfs, o_actions = self._rec_resolve_profile_include(i)

            # merge dotfile keys
            if self._debug:
                self._dbg('Merging dotfiles {} <- {}: {} <- {}'.format(
                    profile, i, dotfiles, o_dfs))
            dotfiles.extend(o_dfs)
            this_profile[self.key_profile_dotfiles] = uniq_list(dotfiles)

            # merge actions keys
            if self._debug:
                self._dbg('Merging actions {} <- {}: {} <- {}'.format(
                    profile, i, actions, o_actions))
            actions.extend(o_actions)
            this_profile[self.key_profile_actions] = uniq_list(actions)

        dotfiles = this_profile.get(self.key_profile_dotfiles, [])
        actions = this_profile.get(self.key_profile_actions, [])

        if self._debug:
            self._dbg('{} dotfiles after include: {}'.format(
                profile, dotfiles))
            self._dbg('{} actions after include: {}'.format(profile, actions))

        # since included items are resolved here
        # we can clear these include
        self.profiles[profile][self.key_profile_include] = []
        return dotfiles, actions
예제 #26
0
    def __init__(self, path, profile=None, addprofiles=[], debug=False):
        """
        config parser
        @path: config file path
        @profile: the selected profile
        @addprofiles: included profiles
        @debug: debug flag
        """
        self._path = os.path.abspath(path)
        self._profile = profile
        self._debug = debug
        self._log = Logger()
        # config needs to be written
        self._dirty = False
        # indicates the config has been updated
        self._dirty_deprecated = False
        # profile variables
        self._profilevarskeys = []
        # included profiles
        self._inc_profiles = addprofiles

        # init the dictionaries
        self.settings = {}
        self.dotfiles = {}
        self.profiles = {}
        self.actions = {}
        self.trans_r = {}
        self.trans_w = {}
        self.variables = {}

        if not os.path.exists(self._path):
            err = 'invalid config path: \"{}\"'.format(path)
            if self._debug:
                self._dbg(err)
            raise YamlException(err)

        self._yaml_dict = self._load_yaml(self._path)
        # live patch deprecated entries
        self._fix_deprecated(self._yaml_dict)

        ##################################################
        # parse the config and variables
        ##################################################

        # parse the "config" block
        self.settings = self._parse_blk_settings(self._yaml_dict)

        # base templater (when no vars/dvars exist)
        self.variables = self._enrich_vars(self.variables, self._profile)
        self._redefine_templater()

        # variables and dynvariables need to be first merged
        # before being templated in order to allow cyclic
        # references between them

        # parse the "variables" block
        var = self._parse_blk_variables(self._yaml_dict)
        self._add_variables(var, template=False)

        # parse the "dynvariables" block
        dvariables = self._parse_blk_dynvariables(self._yaml_dict)
        self._add_variables(dvariables, template=False)

        # now template variables and dynvariables from the same pool
        self._rec_resolve_variables(self.variables)
        # and execute dvariables
        # since this is done after recursively resolving variables
        # and dynvariables this means that variables referencing
        # dynvariables will result with the not executed value
        if dvariables.keys():
            self._shell_exec_dvars(self.variables, keys=dvariables.keys())
        # finally redefine the template
        self._redefine_templater()

        if self._debug:
            self._debug_dict('current variables defined', self.variables)

        # parse the "profiles" block
        self.profiles = self._parse_blk_profiles(self._yaml_dict)

        # include the profile's variables/dynvariables last
        # as it overwrites existing ones
        self._inc_profiles, pv, pvd = self._get_profile_included_vars()
        self._add_variables(pv, prio=True)
        self._add_variables(pvd, shell=True, prio=True)
        self._profilevarskeys.extend(pv.keys())
        self._profilevarskeys.extend(pvd.keys())

        # template variables
        self.variables = self._template_dict(self.variables)
        if self._debug:
            self._debug_dict('current variables defined', self.variables)

        ##################################################
        # template the "include" entries
        ##################################################

        self._template_include_entry()
        if self._debug:
            self._debug_dict('current variables defined', self.variables)

        ##################################################
        # parse the other blocks
        ##################################################

        # parse the "dotfiles" block
        self.dotfiles = self._parse_blk_dotfiles(self._yaml_dict)
        # parse the "actions" block
        self.actions = self._parse_blk_actions(self._yaml_dict)
        # parse the "trans_r" block
        self.trans_r = self._parse_blk_trans_r(self._yaml_dict)
        # parse the "trans_w" block
        self.trans_w = self._parse_blk_trans_w(self._yaml_dict)

        ##################################################
        # import elements
        ##################################################

        # process imported variables (import_variables)
        newvars = self._import_variables()
        self._clear_profile_vars(newvars)
        self._add_variables(newvars)

        # process imported actions (import_actions)
        self._import_actions()
        # process imported profile dotfiles (import)
        self._import_profiles_dotfiles()
        # process imported configs (import_configs)
        self._import_configs()

        # process profile include
        self._resolve_profile_includes()

        # add the current profile variables
        _, pv, pvd = self._get_profile_included_vars()
        self._add_variables(pv, prio=True)
        self._add_variables(pvd, shell=True, prio=True)
        self._profilevarskeys.extend(pv.keys())
        self._profilevarskeys.extend(pvd.keys())

        # resolve variables
        self._clear_profile_vars(newvars)
        self._add_variables(newvars)

        # process profile ALL
        self._resolve_profile_all()
        # patch dotfiles paths
        self._template_dotfiles_paths()

        if self._debug:
            self._dbg('########### {} ###########'.format('final config'))
            self._debug_entries()
예제 #27
0
    def _parse_main_yaml(self, dic):
        """parse the different blocks"""
        self.ori_settings = self._get_entry(dic, self.key_settings)
        self.settings = Settings(None).serialize().get(self.key_settings)
        self.settings.update(self.ori_settings)

        # resolve minimum version
        if self.key_settings_minversion in self.settings:
            minversion = self.settings[self.key_settings_minversion]
            self._check_minversion(minversion)

        # resolve settings paths
        p = self._norm_path(self.settings[self.key_settings_dotpath])
        self.settings[self.key_settings_dotpath] = p
        p = self._norm_path(self.settings[self.key_settings_workdir])
        self.settings[self.key_settings_workdir] = p
        if self.debug:
            self.log.dbg('settings: {}'.format(self.settings))

        # dotfiles
        self.ori_dotfiles = self._get_entry(dic, self.key_dotfiles)
        self.dotfiles = deepcopy(self.ori_dotfiles)
        keys = self.dotfiles.keys()
        if len(keys) != len(list(set(keys))):
            dups = [x for x in keys if x not in list(set(keys))]
            err = 'duplicate dotfile keys found: {}'.format(dups)
            raise YamlException(err)
        self.dotfiles = self._norm_dotfiles(self.dotfiles)
        if self.debug:
            self.log.dbg('dotfiles: {}'.format(self.dotfiles))

        # profiles
        self.ori_profiles = self._get_entry(dic, self.key_profiles)
        self.profiles = deepcopy(self.ori_profiles)
        self.profiles = self._norm_profiles(self.profiles)
        if self.debug:
            self.log.dbg('profiles: {}'.format(self.profiles))

        # actions
        self.ori_actions = self._get_entry(dic,
                                           self.key_actions,
                                           mandatory=False)
        self.actions = deepcopy(self.ori_actions)
        self.actions = self._norm_actions(self.actions)
        if self.debug:
            self.log.dbg('actions: {}'.format(self.actions))

        # trans_r
        key = self.key_trans_r
        if self.old_key_trans_r in dic:
            self.log.warn('\"trans\" is deprecated, please use \"trans_read\"')
            dic[self.key_trans_r] = dic[self.old_key_trans_r]
            del dic[self.old_key_trans_r]
        self.ori_trans_r = self._get_entry(dic, key, mandatory=False)
        self.trans_r = deepcopy(self.ori_trans_r)
        if self.debug:
            self.log.dbg('trans_r: {}'.format(self.trans_r))

        # trans_w
        self.ori_trans_w = self._get_entry(dic,
                                           self.key_trans_w,
                                           mandatory=False)
        self.trans_w = deepcopy(self.ori_trans_w)
        if self.debug:
            self.log.dbg('trans_w: {}'.format(self.trans_w))

        # variables
        self.ori_variables = self._get_entry(dic,
                                             self.key_variables,
                                             mandatory=False)
        if self.debug:
            self.log.dbg('variables: {}'.format(self.ori_variables))

        # dynvariables
        self.ori_dvariables = self._get_entry(dic,
                                              self.key_dvariables,
                                              mandatory=False)
        if self.debug:
            self.log.dbg('dynvariables: {}'.format(self.ori_dvariables))