~aricent/charms/precise/dns/trunk

« back to all changes in this revision

Viewing changes to bin/charm_helpers_sync.py

  • Committer: Anuyog Chauhan
  • Date: 2016-11-16 06:37:23 UTC
  • Revision ID: anuyog.chauhan@aricent.com-20161116063723-z6cyds1di1dmh2g6
Anuyog DNS charm

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
#
 
3
# Copyright 2013 Canonical Ltd.
 
4
 
 
5
# Authors:
 
6
#   Adam Gandelman <adamg@ubuntu.com>
 
7
#
 
8
 
 
9
import logging
 
10
import optparse
 
11
import os
 
12
import subprocess
 
13
import shutil
 
14
import sys
 
15
import tempfile
 
16
import yaml
 
17
 
 
18
from fnmatch import fnmatch
 
19
 
 
20
CHARM_HELPERS_BRANCH = 'lp:charm-helpers'
 
21
 
 
22
 
 
23
def parse_config(conf_file):
 
24
    if not os.path.isfile(conf_file):
 
25
        logging.error('Invalid config file: %s.' % conf_file)
 
26
        return False
 
27
    return yaml.load(open(conf_file).read())
 
28
 
 
29
 
 
30
def clone_helpers(work_dir, branch):
 
31
    dest = os.path.join(work_dir, 'charm-helpers')
 
32
    logging.info('Checking out %s to %s.' % (branch, dest))
 
33
    cmd = ['bzr', 'branch', branch, dest]
 
34
    subprocess.check_call(cmd)
 
35
    return dest
 
36
 
 
37
 
 
38
def _module_path(module):
 
39
    return os.path.join(*module.split('.'))
 
40
 
 
41
 
 
42
def _src_path(src, module):
 
43
    return os.path.join(src, 'charmhelpers', _module_path(module))
 
44
 
 
45
 
 
46
def _dest_path(dest, module):
 
47
    return os.path.join(dest, _module_path(module))
 
48
 
 
49
 
 
50
def _is_pyfile(path):
 
51
    return os.path.isfile(path + '.py')
 
52
 
 
53
 
 
54
def ensure_init(path):
 
55
    '''
 
56
    ensure directories leading up to path are importable, omitting
 
57
    parent directory, eg path='/hooks/helpers/foo'/:
 
58
        hooks/
 
59
        hooks/helpers/__init__.py
 
60
        hooks/helpers/foo/__init__.py
 
61
    '''
 
62
    for d, dirs, files in os.walk(os.path.join(*path.split('/')[:2])):
 
63
        _i = os.path.join(d, '__init__.py')
 
64
        if not os.path.exists(_i):
 
65
            logging.info('Adding missing __init__.py: %s' % _i)
 
66
            open(_i, 'wb').close()
 
67
 
 
68
 
 
69
def sync_pyfile(src, dest):
 
70
    src = src + '.py'
 
71
    src_dir = os.path.dirname(src)
 
72
    logging.info('Syncing pyfile: %s -> %s.' % (src, dest))
 
73
    if not os.path.exists(dest):
 
74
        os.makedirs(dest)
 
75
    shutil.copy(src, dest)
 
76
    if os.path.isfile(os.path.join(src_dir, '__init__.py')):
 
77
        shutil.copy(os.path.join(src_dir, '__init__.py'),
 
78
                    dest)
 
79
    ensure_init(dest)
 
80
 
 
81
 
 
82
def get_filter(opts=None):
 
83
    opts = opts or []
 
84
    if 'inc=*' in opts:
 
85
        # do not filter any files, include everything
 
86
        return None
 
87
 
 
88
    def _filter(dir, ls):
 
89
        incs = [opt.split('=').pop() for opt in opts if 'inc=' in opt]
 
90
        _filter = []
 
91
        for f in ls:
 
92
            _f = os.path.join(dir, f)
 
93
 
 
94
            if not os.path.isdir(_f) and not _f.endswith('.py') and incs:
 
95
                if True not in [fnmatch(_f, inc) for inc in incs]:
 
96
                    logging.debug('Not syncing %s, does not match include '
 
97
                                  'filters (%s)' % (_f, incs))
 
98
                    _filter.append(f)
 
99
                else:
 
100
                    logging.debug('Including file, which matches include '
 
101
                                  'filters (%s): %s' % (incs, _f))
 
102
            elif (os.path.isfile(_f) and not _f.endswith('.py')):
 
103
                logging.debug('Not syncing file: %s' % f)
 
104
                _filter.append(f)
 
105
            elif (os.path.isdir(_f) and not
 
106
                  os.path.isfile(os.path.join(_f, '__init__.py'))):
 
107
                logging.debug('Not syncing directory: %s' % f)
 
108
                _filter.append(f)
 
109
        return _filter
 
110
    return _filter
 
111
 
 
112
 
 
113
def sync_directory(src, dest, opts=None):
 
114
    if os.path.exists(dest):
 
115
        logging.debug('Removing existing directory: %s' % dest)
 
116
        shutil.rmtree(dest)
 
117
    logging.info('Syncing directory: %s -> %s.' % (src, dest))
 
118
 
 
119
    shutil.copytree(src, dest, ignore=get_filter(opts))
 
120
    ensure_init(dest)
 
121
 
 
122
 
 
123
def sync(src, dest, module, opts=None):
 
124
    if os.path.isdir(_src_path(src, module)):
 
125
        sync_directory(_src_path(src, module), _dest_path(dest, module), opts)
 
126
    elif _is_pyfile(_src_path(src, module)):
 
127
        sync_pyfile(_src_path(src, module),
 
128
                    os.path.dirname(_dest_path(dest, module)))
 
129
    else:
 
130
        logging.warn('Could not sync: %s. Neither a pyfile or directory, '
 
131
                     'does it even exist?' % module)
 
132
 
 
133
 
 
134
def parse_sync_options(options):
 
135
    if not options:
 
136
        return []
 
137
    return options.split(',')
 
138
 
 
139
 
 
140
def extract_options(inc, global_options=None):
 
141
    global_options = global_options or []
 
142
    if global_options and isinstance(global_options, basestring):
 
143
        global_options = [global_options]
 
144
    if '|' not in inc:
 
145
        return (inc, global_options)
 
146
    inc, opts = inc.split('|')
 
147
    return (inc, parse_sync_options(opts) + global_options)
 
148
 
 
149
 
 
150
def sync_helpers(include, src, dest, options=None):
 
151
    if not os.path.isdir(dest):
 
152
        os.mkdir(dest)
 
153
 
 
154
    global_options = parse_sync_options(options)
 
155
 
 
156
    for inc in include:
 
157
        if isinstance(inc, str):
 
158
            inc, opts = extract_options(inc, global_options)
 
159
            sync(src, dest, inc, opts)
 
160
        elif isinstance(inc, dict):
 
161
            # could also do nested dicts here.
 
162
            for k, v in inc.iteritems():
 
163
                if isinstance(v, list):
 
164
                    for m in v:
 
165
                        inc, opts = extract_options(m, global_options)
 
166
                        sync(src, dest, '%s.%s' % (k, inc), opts)
 
167
 
 
168
if __name__ == '__main__':
 
169
    parser = optparse.OptionParser()
 
170
    parser.add_option('-c', '--config', action='store', dest='config',
 
171
                      default=None, help='helper config file')
 
172
    parser.add_option('-D', '--debug', action='store_true', dest='debug',
 
173
                      default=False, help='debug')
 
174
    parser.add_option('-b', '--branch', action='store', dest='branch',
 
175
                      help='charm-helpers bzr branch (overrides config)')
 
176
    parser.add_option('-d', '--destination', action='store', dest='dest_dir',
 
177
                      help='sync destination dir (overrides config)')
 
178
    (opts, args) = parser.parse_args()
 
179
 
 
180
    if opts.debug:
 
181
        logging.basicConfig(level=logging.DEBUG)
 
182
    else:
 
183
        logging.basicConfig(level=logging.INFO)
 
184
 
 
185
    if opts.config:
 
186
        logging.info('Loading charm helper config from %s.' % opts.config)
 
187
        config = parse_config(opts.config)
 
188
        if not config:
 
189
            logging.error('Could not parse config from %s.' % opts.config)
 
190
            sys.exit(1)
 
191
    else:
 
192
        config = {}
 
193
 
 
194
    if 'branch' not in config:
 
195
        config['branch'] = CHARM_HELPERS_BRANCH
 
196
    if opts.branch:
 
197
        config['branch'] = opts.branch
 
198
    if opts.dest_dir:
 
199
        config['destination'] = opts.dest_dir
 
200
 
 
201
    if 'destination' not in config:
 
202
        logging.error('No destination dir. specified as option or config.')
 
203
        sys.exit(1)
 
204
 
 
205
    if 'include' not in config:
 
206
        if not args:
 
207
            logging.error('No modules to sync specified as option or config.')
 
208
            sys.exit(1)
 
209
        config['include'] = []
 
210
        [config['include'].append(a) for a in args]
 
211
 
 
212
    sync_options = None
 
213
    if 'options' in config:
 
214
        sync_options = config['options']
 
215
    tmpd = tempfile.mkdtemp()
 
216
    try:
 
217
        checkout = clone_helpers(tmpd, config['branch'])
 
218
        sync_helpers(config['include'], checkout, config['destination'],
 
219
                     options=sync_options)
 
220
    except Exception, e:
 
221
        logging.error("Could not sync: %s" % e)
 
222
        raise e
 
223
    finally:
 
224
        logging.debug('Cleaning up %s' % tmpd)
 
225
        shutil.rmtree(tmpd)