Debmirror initial commit

Change-Id: I5c3b9f9ec0e8e882f4d1c4616a5b86598c04cf4b
diff --git a/_states/debmirror.py b/_states/debmirror.py
new file mode 100644
index 0000000..2e5533f
--- /dev/null
+++ b/_states/debmirror.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python
+"""
+Management of debmirror
+=======================
+
+Create debian mirrors, using debmirror
+--------------------------------------
+
+.. code-block:: yaml
+    debmirror_test1_present:
+      debmirror.mirror_present:
+        - name: test1
+
+"""
+import logging
+import os
+from functools import wraps
+from salt.exceptions import CommandExecutionError, SaltInvocationError
+log = logging.getLogger(__name__)
+
+def __virtual__():
+    '''
+    Only load this module if debmirror is installed with deps.
+    '''
+    return 'debmirror'
+
+def _test_call(method):
+    (resource, functionality) = method.func_name.split('_')
+    if functionality == 'present':
+        functionality = 'updated'
+    else:
+        functionality = 'removed'
+
+    @wraps(method)
+    def check_for_testing(name, *args, **kwargs):
+        if __opts__.get('test', None):
+            return _no_change(name, resource, test=functionality)
+        return method(name, *args, **kwargs)
+    return check_for_testing
+
+def _created(name, resource, resource_definition={}):
+    changes_dict = {'name': name,
+                    'changes': resource_definition,
+                    'result': True,
+                    'comment': '{0} {1} created'.format(resource, name)}
+    return changes_dict
+
+def _failed(name, resource, resource_definition={}):
+    changes_dict = {'name': name,
+                    'changes': resource_definition,
+                    'result': False,
+                    'comment': '{0} {1} failed to create'.format(resource, name)}
+    return changes_dict
+
+def _no_change(name, resource, test=False):
+    changes_dict = {'name': name,
+                    'changes': {},
+                    'result': True}
+    if test:
+        changes_dict['comment'] = \
+            '{0} {1} will be {2}'.format(resource, name, test)
+    else:
+        changes_dict['comment'] = \
+            '{0} {1} is in correct state'.format(resource, name)
+    return changes_dict
+
+def _check_state(name, tgt):
+    lock_file = _get_target_path(name,tgt)['lock_file']
+    if os.path.isfile(lock_file) and not tgt.get('force', False) :
+      	return _no_change(name, '{} exist=>repo locked.'.format(lock_file))
+    return False
+
+def _get_target_path(name,tgt):
+    if not tgt.get('target_dir',False):
+        raise SaltInvocationError('Argument "target_dir" is mandatory! ')
+    return {'target_dir': tgt.get('target_dir'),
+            'lock_file': tgt.get('lock_file', os.path.join(tgt.get('target_dir'), '.lockmirror')),
+            'log_file':  tgt.get('log_file', '/var/log/debmirror_'.join(name)) }
+
+def _get_cmdline(name,tgt):
+    cmdline = " debmirror "
+    if tgt.get('extra_flags'):
+      cmdline += ' '.join(tgt['extra_flags'])
+    if tgt.get('dist'):
+      cmdline += ' --dist=' + ",".join(tgt['dist'])
+    if tgt.get('section'):
+      cmdline += ' --section=' + ",".join(tgt['section'])
+    if tgt.get('method'):
+      cmdline += ' --method=' + tgt.get('method')
+    if tgt.get('mirror_host'):
+      cmdline += ' --host="{}"'.format(tgt.get('mirror_host'))
+    if tgt.get('mirror_root'):
+      cmdline += ' --root="{}"'.format(tgt.get('mirror_root'))
+    if tgt.get('arch', 'amd64'):
+      cmdline += ' --arch=' + ','.join(tgt.get('arch'))
+    if tgt.get('filter'):
+      for key, value in enumerate(sorted(tgt['filter'])):
+          cmdline += " " + tgt['filter'][value]
+    if tgt.get('target_dir',False):
+      cmdline +=  ' ' + _get_target_path(name,tgt)['target_dir']
+    return cmdline
+
+def _update_mirror(name,tgt):
+    # Remove old lock file, is was.
+    lock_file = _get_target_path(name,tgt)['lock_file']
+    if os.path.isfile(lock_file):
+        log.debug("Removing lockfile:{} for mirror{}".format(lock_file,name))
+        __states__['file.absent'](lock_file)
+    cmdline = _get_cmdline(name,tgt)
+    # init file logger
+    l_dir = os.path.dirname(tgt['log_file'])
+    if not os.path.isdir(l_dir):
+        __salt__['file.makedirs'](l_dir)
+    fh = logging.FileHandler("{0}".format(_get_target_path(name,tgt)['log_file']))
+    fh.setLevel(logging.DEBUG)
+    fh_format = logging.Formatter('%(asctime)s - %(lineno)d - %(levelname)-8s - %(message)s')
+    fh.setFormatter(fh_format)
+    log2file = logging.getLogger("debmirror")
+    log2file.addHandler(fh)
+    result = __salt__['cmd.run_all'](cmdline, redirect_stderr=True)
+    log2file.debug(result['stdout'])
+    # destroy file logger
+    for i in list(log2file.handlers):
+        log2file.removeHandler(i)
+        i.flush()
+        i.close()
+    if result['retcode'] != 0:
+        #raise CommandExecutionError(result['stderr'])
+        return _failed(name, "debmirror failed.Reason {0}".format(result['stderr']))
+    if tgt.get('lock_target',None):
+        __states__['file.touch'](lock_file)
+    return _created(name, "Mirror {0} created.".format(name) )
+
+@_test_call
+def mirror_present(name, **kwargs):
+    '''
+
+    :param name: mirror key name
+    '''
+    try:
+        tgt = __salt__['config.get']('debmirror')['client']['mirrors'][name]
+    except KeyError:
+        comment ='Mirror "{0}" not exist in configurathion,skipping..'.format(name)
+        return _no_change(name, comment)
+
+    current_state = _check_state(name,tgt)
+    if not current_state:
+       return _update_mirror(name,tgt)
+    return current_state