Skip to content

Instantly share code, notes, and snippets.

@mcansky
Created June 18, 2015 14:12
Show Gist options
  • Save mcansky/07a86eab5213dcf80e46 to your computer and use it in GitHub Desktop.
Save mcansky/07a86eab5213dcf80e46 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
Check that apt lock is off or on. If on, wait up to 60 seconds before trying
again and returning success (if released) or failure.
Author: Thomas R. R. Riboulet <riboulet@gmail.com>
'''
import logging
import json
import time
import fcntl
# Set up logging
LOG = logging.getLogger(__name__)
def _apt_locked():
'''
Try locking the apt lock
True if locked
False if unlocked
'''
with open('/var/lib/dpkg/lock', 'w') as handle:
try:
fcntl.lockf(handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
return False
except IOError:
return True
def check_lock():
'''
Check lock state and return success if it's free or released under 60 seconds
'''
if _apt_locked():
LOG.info("APT lock ON, waiting 60 seconds")
time.sleep(60)
if _apt_locked():
LOG.info("APT lock still ON, exiting")
return False
LOG.info("APT lock OFF")
return True
if __name__ == "__main__":
print check_lock()
@Reiner030
Copy link

Ah, parameter adding was very easy, now also with comment which shows waiting time/retries:

--- base/_modules/apt_lock_wait.py.orig 2016-02-12 19:34:07.292498856 +0000
+++ base/_modules/apt_lock_wait.py      2016-02-14 12:59:22.519371134 +0000
@@ -1,8 +1,8 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 '''
-Check that apt lock is off or on. If on, wait up to 60 seconds before trying
-again and returning success (if released) or failure.
+Check that apt lock is off or on. If on, wait up to 10 times 2 seconds before trying
+again and returning success (if released) or failure after last check.

 Author: Thomas R. R. Riboulet <riboulet@gmail.com>

@@ -29,19 +29,34 @@
                except IOError:
                        return True

-def check_lock():
+def check_lock(name='check_apt_lock', retries=10, timeout=2):
        '''
-       Check lock state and return success if it's free or released under 60 seconds
+       Check lock state and return success if it's free or released under timeout=2 seconds
        '''
-       if _apt_locked():
-               LOG.info("APT lock ON, waiting 60 seconds")
-               time.sleep(60)
-               if _apt_locked():
-                       LOG.info("APT lock still ON, exiting")
-                       return False
+       ret = {'name': name, 'result': False, 'comment': ''}

-       LOG.info("APT lock OFF")
-       return True
+       count = 0
+       while count < retries and _apt_locked():
+               LOG.info('APT lock ON, waiting {0} seconds (#{1})'.format(timeout, count))
+               time.sleep(timeout)
+               count += 1
+
+       if count >= retries and _apt_locked():
+               LOG.info('APT lock ON, exiting after {0} seconds (#{1})'.format(timeout*count, count))
+               ret['result'] = False
+               ret['comment'] = 'APT lock ON, exiting after {0} seconds (#{1})'.format(timeout*count, count)
+               return ret
+
+       if count > 0:
+               LOG.info('APT lock OFF, exiting after {0} seconds (#{1})'.format(timeout*count, count))
+               ret['result'] = True
+               ret['comment'] = 'APT lock OFF, exiting after {0} seconds (#{1})'.format(timeout*count, count)
+               return ret
+
+       LOG.info('APT lock OFF')
+       ret['result'] = True
+       ret['comment'] = 'APT lock OFF'
+       return ret

 if __name__ == "__main__":
        print check_lock()

One "big" problem is still left: the state is always run as "changed" when defined this way:

jenkins-package:
  module.run:
    - name: apt_lock_wait.check_lock
  pkg.installed:
    - name: jenkins

But it seems that this can only changed by having another module type:

From last answer in: http://grokbase.com/t/gg/salt-users/14b6rzhwzb/calling-an-execution-module-results-in-changed-everytime

What you really want to look into is using a custom state module, instead of a custom execution module. You then have full control over the return data structure.
More info here: http://docs.saltstack.com/en/latest/ref/states/writing.html

Or can this module called differently ?

EDIT: easy solution: creating an onlyif condition runs the module only if locks exists

jenkins-package:
  module.run:
    - name: apt_lock_wait.check_lock
    - onlyif: test -n "$(lslocks -r -o PATH | grep /var/lib/dpkg/lock)"
  pkg.installed:
    - name: jenkins

@Reiner030
Copy link

Seems some kernel/other package updates broke something with lslocks so it took very long to check:

# time (lslocks -r -o PATH | grep /var/lib/dpkg/lock)

    real    0m16.080s
    user    0m0.976s
    sys     0m15.104s

A nice solution I found is checking it with lsof instead:

# time (lsof | grep -q /var/lib/dpkg/lock)

    real    0m2.715s
    user    0m1.364s
    sys     0m1.184s

but for this the package has to be installed first.
So I decided to put two different checks before which programs are setup by default:

  • test (from bash/shell) to see if lockfile exists
  • fuser to check if a program is accessing it
jenkins-package:
  module.run:
    - name: apt_lock_wait.check_lock
    - onlyif: test -f /var/lib/dpkg/lock && `fuser /var/lib/dpkg/lock >/dev/null 2>&1` && `lslocks -r -o PATH | grep -q /var/lib/dpkg/lock`
  pkg.installed:
    - name: jenkins

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment