blob: 6001df1498d870da7a303eb5a5ddaa7df972f0eb [file] [log] [blame]
azvyagintseve5c4e832018-01-25 20:02:46 +02001#!/usr/bin/env python
2"""
3Management of debmirror
4=======================
5
6Create debian mirrors, using debmirror
7--------------------------------------
8
9.. code-block:: yaml
10 debmirror_test1_present:
11 debmirror.mirror_present:
12 - name: test1
13
14"""
15import logging
16import os
17from functools import wraps
18from salt.exceptions import CommandExecutionError, SaltInvocationError
19log = logging.getLogger(__name__)
20
azvyagintsev73720db2018-06-20 17:28:09 +030021
azvyagintseve5c4e832018-01-25 20:02:46 +020022def __virtual__():
23 '''
24 Only load this module if debmirror is installed with deps.
25 '''
26 return 'debmirror'
27
azvyagintsev73720db2018-06-20 17:28:09 +030028
azvyagintseve5c4e832018-01-25 20:02:46 +020029def _test_call(method):
30 (resource, functionality) = method.func_name.split('_')
31 if functionality == 'present':
32 functionality = 'updated'
33 else:
34 functionality = 'removed'
35
36 @wraps(method)
37 def check_for_testing(name, *args, **kwargs):
38 if __opts__.get('test', None):
39 return _no_change(name, resource, test=functionality)
40 return method(name, *args, **kwargs)
azvyagintsev73720db2018-06-20 17:28:09 +030041
azvyagintseve5c4e832018-01-25 20:02:46 +020042 return check_for_testing
43
azvyagintsev73720db2018-06-20 17:28:09 +030044
azvyagintseve5c4e832018-01-25 20:02:46 +020045def _created(name, resource, resource_definition={}):
46 changes_dict = {'name': name,
47 'changes': resource_definition,
48 'result': True,
49 'comment': '{0} {1} created'.format(resource, name)}
50 return changes_dict
51
azvyagintsev73720db2018-06-20 17:28:09 +030052
azvyagintseve5c4e832018-01-25 20:02:46 +020053def _failed(name, resource, resource_definition={}):
54 changes_dict = {'name': name,
55 'changes': resource_definition,
56 'result': False,
azvyagintsev73720db2018-06-20 17:28:09 +030057 'comment': '{0} {1} failed to create'.format(resource,
58 name)}
azvyagintseve5c4e832018-01-25 20:02:46 +020059 return changes_dict
60
azvyagintsev73720db2018-06-20 17:28:09 +030061
azvyagintseve5c4e832018-01-25 20:02:46 +020062def _no_change(name, resource, test=False):
63 changes_dict = {'name': name,
64 'changes': {},
65 'result': True}
66 if test:
67 changes_dict['comment'] = \
68 '{0} {1} will be {2}'.format(resource, name, test)
69 else:
70 changes_dict['comment'] = \
71 '{0} {1} is in correct state'.format(resource, name)
72 return changes_dict
73
azvyagintsev73720db2018-06-20 17:28:09 +030074
azvyagintseve5c4e832018-01-25 20:02:46 +020075def _check_state(name, tgt):
azvyagintsev73720db2018-06-20 17:28:09 +030076 lock_file = _get_target_path(name, tgt)['lock_file']
77 if os.path.isfile(lock_file) and not tgt.get('force', False):
azvyagintsevb80d6222018-02-19 15:53:40 +020078 return _no_change(name, '{} exist=>repo locked.'.format(lock_file))
azvyagintseve5c4e832018-01-25 20:02:46 +020079 return False
80
azvyagintsev73720db2018-06-20 17:28:09 +030081
82def _get_target_path(name, tgt):
83 if not tgt.get('target_dir', False):
azvyagintseve5c4e832018-01-25 20:02:46 +020084 raise SaltInvocationError('Argument "target_dir" is mandatory! ')
Denis Egorenkoe5b036d2019-09-20 14:15:54 +040085
86 out = {'target_dir': tgt.get('target_dir'),
azvyagintsev73720db2018-06-20 17:28:09 +030087 'lock_file': tgt.get('lock_file',
88 os.path.join(tgt.get('target_dir'),
89 '.lockmirror')),
90 'log_file': tgt.get('log_file', '/var/log/debmirror_'.join(name))}
azvyagintseve5c4e832018-01-25 20:02:46 +020091
Denis Egorenkoe5b036d2019-09-20 14:15:54 +040092 if tgt.get('cache_dir', False):
93 out['cache_dir'] = tgt.get('cache_dir')
94 out['cache_ready_file'] = os.path.join(out['cache_dir'], '.ready')
95
96 return out
97
azvyagintsev73720db2018-06-20 17:28:09 +030098
99def _get_env(tgt):
100 env = {}
101 for k in ['http_proxy', 'https_proxy', 'ftp_proxy', 'rsync_proxy']:
102 if tgt.get(k, False):
103 env[k] = tgt[k]
104 env[k.upper()] = env[k]
105 if tgt.get('no_proxy', False):
106 env['no_proxy'] = ','.join(str(x) for x in tgt['no_proxy'])
107 env['no_proxy'.upper()] = env['no_proxy']
108 return env
109
110
Denis Egorenkoe5b036d2019-09-20 14:15:54 +0400111def _check_mirror_cache(file_name):
112 return os.path.isfile(file_name)
113
114
115def _cache_copied(file_name, target_dir, action='check'):
116 pattern = '%s - copied\n' % target_dir
117 if action == 'add':
118 with open(file_name, 'a+') as f:
119 f.write(pattern)
120 return True
121 else:
122 with open(file_name, 'r') as f:
123 cache_copied = f.readlines()
124 if pattern in cache_copied:
125 return True
126 else:
127 return False
Denis Egorenko964ba9a2018-10-31 17:22:05 +0400128
129
azvyagintsev73720db2018-06-20 17:28:09 +0300130def _get_cmdline(name, tgt):
azvyagintseve5c4e832018-01-25 20:02:46 +0200131 cmdline = " debmirror "
132 if tgt.get('extra_flags'):
azvyagintsev73720db2018-06-20 17:28:09 +0300133 cmdline += ' '.join(tgt['extra_flags'])
Denis Egorenko39db2a62018-10-30 14:37:17 +0400134 if tgt.get('rsync_options'):
135 rsync_options = tgt.get('rsync_options')
136 if tgt.get('rsync_options_defaults', True):
137 rsync_options = ['-aL', '--partial'] + rsync_options
138 cmdline += " --rsync-options='" + ' '.join(rsync_options) + "'"
azvyagintseve5c4e832018-01-25 20:02:46 +0200139 if tgt.get('dist'):
azvyagintsev73720db2018-06-20 17:28:09 +0300140 cmdline += ' --dist=' + ",".join(tgt['dist'])
azvyagintseve5c4e832018-01-25 20:02:46 +0200141 if tgt.get('section'):
azvyagintsev73720db2018-06-20 17:28:09 +0300142 cmdline += ' --section=' + ",".join(tgt['section'])
azvyagintseve5c4e832018-01-25 20:02:46 +0200143 if tgt.get('method'):
azvyagintsev73720db2018-06-20 17:28:09 +0300144 cmdline += ' --method=' + tgt.get('method')
azvyagintseve5c4e832018-01-25 20:02:46 +0200145 if tgt.get('mirror_host'):
azvyagintsev73720db2018-06-20 17:28:09 +0300146 cmdline += ' --host="{}"'.format(tgt.get('mirror_host'))
azvyagintseve5c4e832018-01-25 20:02:46 +0200147 if tgt.get('mirror_root'):
azvyagintsev73720db2018-06-20 17:28:09 +0300148 cmdline += ' --root="{}"'.format(tgt.get('mirror_root'))
azvyagintseve5c4e832018-01-25 20:02:46 +0200149 if tgt.get('arch', 'amd64'):
azvyagintsev73720db2018-06-20 17:28:09 +0300150 cmdline += ' --arch=' + ','.join(tgt.get('arch'))
azvyagintsev6e410f92018-03-07 23:35:05 +0200151 if tgt.get('exclude_deb_section'):
azvyagintsev73720db2018-06-20 17:28:09 +0300152 for section in tgt['exclude_deb_section']:
153 cmdline += " --exclude-deb-section='" + section + "'"
azvyagintseve5c4e832018-01-25 20:02:46 +0200154 if tgt.get('filter'):
azvyagintsev73720db2018-06-20 17:28:09 +0300155 for key, value in enumerate(sorted(tgt['filter'])):
156 cmdline += " " + tgt['filter'][value]
157 if tgt.get('target_dir', False):
Denis Egorenkoe5b036d2019-09-20 14:15:54 +0400158 targets = _get_target_path(name, tgt)
159 target_dir = targets['target_dir']
Denis Egorenko964ba9a2018-10-31 17:22:05 +0400160 if tgt.get('cache_dir'):
Denis Egorenkoe5b036d2019-09-20 14:15:54 +0400161 cache_dir = targets['cache_dir']
162 if _check_mirror_cache(targets['cache_ready_file']):
Denis Egorenko964ba9a2018-10-31 17:22:05 +0400163 cmdline += ' ' + target_dir
164 else:
165 cmdline += ' ' + cache_dir
166 else:
167 cmdline += ' ' + target_dir
azvyagintseve5c4e832018-01-25 20:02:46 +0200168 return cmdline
169
azvyagintsev73720db2018-06-20 17:28:09 +0300170
171def _update_mirror(name, tgt):
Denis Egorenkoe5b036d2019-09-20 14:15:54 +0400172 targetPaths = _get_target_path(name, tgt)
azvyagintseve5c4e832018-01-25 20:02:46 +0200173 # Remove old lock file, is was.
Denis Egorenkoe5b036d2019-09-20 14:15:54 +0400174 lock_file = targetPaths['lock_file']
azvyagintseve5c4e832018-01-25 20:02:46 +0200175 if os.path.isfile(lock_file):
Denis Egorenkoe5b036d2019-09-20 14:15:54 +0400176 log.debug("Removing lockfile: {} for mirror {}".format(
177 lock_file, name))
azvyagintseve5c4e832018-01-25 20:02:46 +0200178 __states__['file.absent'](lock_file)
azvyagintsev73720db2018-06-20 17:28:09 +0300179 cmdline = _get_cmdline(name, tgt)
180 # fetch ENV params for proxy
181 env_vars = _get_env(tgt)
azvyagintseve5c4e832018-01-25 20:02:46 +0200182 # init file logger
183 l_dir = os.path.dirname(tgt['log_file'])
184 if not os.path.isdir(l_dir):
azvyagintsevf5fc6532018-02-21 22:17:02 +0200185 __salt__['file.makedirs'](l_dir + '/')
azvyagintsev73720db2018-06-20 17:28:09 +0300186 fh = logging.FileHandler(
Denis Egorenkoe5b036d2019-09-20 14:15:54 +0400187 "{0}".format(targetPaths['log_file']))
azvyagintseve5c4e832018-01-25 20:02:46 +0200188 fh.setLevel(logging.DEBUG)
azvyagintsev73720db2018-06-20 17:28:09 +0300189 fh_format = logging.Formatter(
190 '%(asctime)s - %(lineno)d - %(levelname)-8s - %(message)s')
azvyagintseve5c4e832018-01-25 20:02:46 +0200191 fh.setFormatter(fh_format)
192 log2file = logging.getLogger("debmirror")
193 log2file.addHandler(fh)
Denis Egorenko964ba9a2018-10-31 17:22:05 +0400194 # check cache usage
195 if tgt.get('cache_dir'):
Denis Egorenkoe5b036d2019-09-20 14:15:54 +0400196 cache_dir = targetPaths['cache_dir']
197 cache_ready = targetPaths['cache_ready_file']
198 target_dir = targetPaths['target_dir']
Denis Egorenko964ba9a2018-10-31 17:22:05 +0400199 cpline = "find . -name *.deb -exec cp -rlf --parent {} %s \\;" % (
200 target_dir)
Denis Egorenkoe5b036d2019-09-20 14:15:54 +0400201
202 # if cache exists and not copied - copy and run sync over cache
203 # if cache exists and copied - do nothing
204 if _check_mirror_cache(cache_ready):
205 if not _cache_copied(cache_ready, target_dir):
206 result = __salt__['cmd.run_all'](cpline, redirect_stderr=True,
207 cwd=cache_dir, env=env_vars)
Denis Egorenko964ba9a2018-10-31 17:22:05 +0400208 log2file.debug(result['stdout'])
Denis Egorenkoe5b036d2019-09-20 14:15:54 +0400209
210 if result['retcode'] != 0:
211 return _failed(name,
212 "debmirror failed to copy cache. Reason {0}".format(
213 result['stderr']))
214
215 _cache_copied(cache_ready, target_dir, 'add')
216 # if cache does not exist - create cache and copy
Denis Egorenko964ba9a2018-10-31 17:22:05 +0400217 else:
218 result = __salt__['cmd.run_all'](cmdline, redirect_stderr=True,
219 env=env_vars)
Denis Egorenkoe5b036d2019-09-20 14:15:54 +0400220 log2file.debug(result['stdout'])
Denis Egorenko77846422019-09-10 17:27:21 +0400221
Denis Egorenkoe5b036d2019-09-20 14:15:54 +0400222 if result['retcode'] != 0:
223 return _failed(name,
224 "debmirror failed. Reason {0}".format(
225 result['stderr']))
226
227 __states__['file.touch'](cache_ready)
228 result = __salt__['cmd.run_all'](cpline, redirect_stderr=True,
229 cwd=cache_dir, env=env_vars)
230 log2file.debug(result['stdout'])
231
232 if result['retcode'] != 0:
233 return _failed(name,
234 "debmirror failed to copy cache. Reason {0}".format(
235 result['stderr']))
236
237 _cache_copied(cache_ready, target_dir, 'add')
238 cmdline = _get_cmdline(name, tgt)
239
240 result = __salt__['cmd.run_all'](cmdline, redirect_stderr=True,
241 env=env_vars)
azvyagintseve5c4e832018-01-25 20:02:46 +0200242 log2file.debug(result['stdout'])
243 # destroy file logger
244 for i in list(log2file.handlers):
245 log2file.removeHandler(i)
246 i.flush()
247 i.close()
248 if result['retcode'] != 0:
azvyagintsev73720db2018-06-20 17:28:09 +0300249 # raise CommandExecutionError(result['stderr'])
250 return _failed(name,
251 "debmirror failed.Reason {0}".format(result['stderr']))
252 if tgt.get('lock_target', None):
azvyagintseve5c4e832018-01-25 20:02:46 +0200253 __states__['file.touch'](lock_file)
azvyagintsev73720db2018-06-20 17:28:09 +0300254 return _created(name, "Mirror {0} created.".format(name))
255
azvyagintseve5c4e832018-01-25 20:02:46 +0200256
257@_test_call
258def mirror_present(name, **kwargs):
259 '''
260
261 :param name: mirror key name
262 '''
263 try:
264 tgt = __salt__['config.get']('debmirror')['client']['mirrors'][name]
265 except KeyError:
azvyagintsev73720db2018-06-20 17:28:09 +0300266 comment = 'Mirror "{0}" not exist in configurathion,skipping..'.format(
267 name)
azvyagintseve5c4e832018-01-25 20:02:46 +0200268 return _no_change(name, comment)
269
azvyagintsev73720db2018-06-20 17:28:09 +0300270 current_state = _check_state(name, tgt)
azvyagintseve5c4e832018-01-25 20:02:46 +0200271 if not current_state:
azvyagintsev73720db2018-06-20 17:28:09 +0300272 return _update_mirror(name, tgt)
azvyagintseve5c4e832018-01-25 20:02:46 +0200273 return current_state