~bbaqar/charms/trusty/neutron-api-plumgrid/temp

« back to all changes in this revision

Viewing changes to hooks/charmhelpers/contrib/hardening/utils.py

  • Committer: bbaqar at plumgrid
  • Date: 2016-04-25 09:05:55 UTC
  • mfrom: (18.1.7 neutron-api-plumgrid)
  • Revision ID: bbaqar@plumgrid.com-20160425090555-yl82ba6bhwjqkwr9
Merge: Liberty/Mitaka support

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2016 Canonical Limited.
 
2
#
 
3
# This file is part of charm-helpers.
 
4
#
 
5
# charm-helpers is free software: you can redistribute it and/or modify
 
6
# it under the terms of the GNU Lesser General Public License version 3 as
 
7
# published by the Free Software Foundation.
 
8
#
 
9
# charm-helpers is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU Lesser General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU Lesser General Public License
 
15
# along with charm-helpers.  If not, see <http://www.gnu.org/licenses/>.
 
16
 
 
17
import glob
 
18
import grp
 
19
import os
 
20
import pwd
 
21
import six
 
22
import yaml
 
23
 
 
24
from charmhelpers.core.hookenv import (
 
25
    log,
 
26
    DEBUG,
 
27
    INFO,
 
28
    WARNING,
 
29
    ERROR,
 
30
)
 
31
 
 
32
 
 
33
# Global settings cache. Since each hook fire entails a fresh module import it
 
34
# is safe to hold this in memory and not risk missing config changes (since
 
35
# they will result in a new hook fire and thus re-import).
 
36
__SETTINGS__ = {}
 
37
 
 
38
 
 
39
def _get_defaults(modules):
 
40
    """Load the default config for the provided modules.
 
41
 
 
42
    :param modules: stack modules config defaults to lookup.
 
43
    :returns: modules default config dictionary.
 
44
    """
 
45
    default = os.path.join(os.path.dirname(__file__),
 
46
                           'defaults/%s.yaml' % (modules))
 
47
    return yaml.safe_load(open(default))
 
48
 
 
49
 
 
50
def _get_schema(modules):
 
51
    """Load the config schema for the provided modules.
 
52
 
 
53
    NOTE: this schema is intended to have 1-1 relationship with they keys in
 
54
    the default config and is used a means to verify valid overrides provided
 
55
    by the user.
 
56
 
 
57
    :param modules: stack modules config schema to lookup.
 
58
    :returns: modules default schema dictionary.
 
59
    """
 
60
    schema = os.path.join(os.path.dirname(__file__),
 
61
                          'defaults/%s.yaml.schema' % (modules))
 
62
    return yaml.safe_load(open(schema))
 
63
 
 
64
 
 
65
def _get_user_provided_overrides(modules):
 
66
    """Load user-provided config overrides.
 
67
 
 
68
    :param modules: stack modules to lookup in user overrides yaml file.
 
69
    :returns: overrides dictionary.
 
70
    """
 
71
    overrides = os.path.join(os.environ['JUJU_CHARM_DIR'],
 
72
                             'hardening.yaml')
 
73
    if os.path.exists(overrides):
 
74
        log("Found user-provided config overrides file '%s'" %
 
75
            (overrides), level=DEBUG)
 
76
        settings = yaml.safe_load(open(overrides))
 
77
        if settings and settings.get(modules):
 
78
            log("Applying '%s' overrides" % (modules), level=DEBUG)
 
79
            return settings.get(modules)
 
80
 
 
81
        log("No overrides found for '%s'" % (modules), level=DEBUG)
 
82
    else:
 
83
        log("No hardening config overrides file '%s' found in charm "
 
84
            "root dir" % (overrides), level=DEBUG)
 
85
 
 
86
    return {}
 
87
 
 
88
 
 
89
def _apply_overrides(settings, overrides, schema):
 
90
    """Get overrides config overlayed onto modules defaults.
 
91
 
 
92
    :param modules: require stack modules config.
 
93
    :returns: dictionary of modules config with user overrides applied.
 
94
    """
 
95
    if overrides:
 
96
        for k, v in six.iteritems(overrides):
 
97
            if k in schema:
 
98
                if schema[k] is None:
 
99
                    settings[k] = v
 
100
                elif type(schema[k]) is dict:
 
101
                    settings[k] = _apply_overrides(settings[k], overrides[k],
 
102
                                                   schema[k])
 
103
                else:
 
104
                    raise Exception("Unexpected type found in schema '%s'" %
 
105
                                    type(schema[k]), level=ERROR)
 
106
            else:
 
107
                log("Unknown override key '%s' - ignoring" % (k), level=INFO)
 
108
 
 
109
    return settings
 
110
 
 
111
 
 
112
def get_settings(modules):
 
113
    global __SETTINGS__
 
114
    if modules in __SETTINGS__:
 
115
        return __SETTINGS__[modules]
 
116
 
 
117
    schema = _get_schema(modules)
 
118
    settings = _get_defaults(modules)
 
119
    overrides = _get_user_provided_overrides(modules)
 
120
    __SETTINGS__[modules] = _apply_overrides(settings, overrides, schema)
 
121
    return __SETTINGS__[modules]
 
122
 
 
123
 
 
124
def ensure_permissions(path, user, group, permissions, maxdepth=-1):
 
125
    """Ensure permissions for path.
 
126
 
 
127
    If path is a file, apply to file and return. If path is a directory,
 
128
    apply recursively (if required) to directory contents and return.
 
129
 
 
130
    :param user: user name
 
131
    :param group: group name
 
132
    :param permissions: octal permissions
 
133
    :param maxdepth: maximum recursion depth. A negative maxdepth allows
 
134
                     infinite recursion and maxdepth=0 means no recursion.
 
135
    :returns: None
 
136
    """
 
137
    if not os.path.exists(path):
 
138
        log("File '%s' does not exist - cannot set permissions" % (path),
 
139
            level=WARNING)
 
140
        return
 
141
 
 
142
    _user = pwd.getpwnam(user)
 
143
    os.chown(path, _user.pw_uid, grp.getgrnam(group).gr_gid)
 
144
    os.chmod(path, permissions)
 
145
 
 
146
    if maxdepth == 0:
 
147
        log("Max recursion depth reached - skipping further recursion",
 
148
            level=DEBUG)
 
149
        return
 
150
    elif maxdepth > 0:
 
151
        maxdepth -= 1
 
152
 
 
153
    if os.path.isdir(path):
 
154
        contents = glob.glob("%s/*" % (path))
 
155
        for c in contents:
 
156
            ensure_permissions(c, user=user, group=group,
 
157
                               permissions=permissions, maxdepth=maxdepth)