From 07dc2598aff11fa3461596ace739f9c6edb3ca3d Mon Sep 17 00:00:00 2001 From: Filipp Lepalaan Date: Mon, 26 Sep 2016 16:25:03 +0300 Subject: Added workaround for EULA-images --- README.md | 50 ++++++++++++++++++++++++++++++++ machammer/__init__.py | 2 +- machammer/functions.py | 11 +++++-- machammer/tests.py | 74 ----------------------------------------------- tests.py | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 138 insertions(+), 77 deletions(-) delete mode 100755 machammer/tests.py create mode 100755 tests.py diff --git a/README.md b/README.md index 1ba8bc6..58ad589 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,56 @@ Save that in `install.py` (or whatever you want) and just run with `sudo python Pro tip: put install.py under version control! +### Reusing admin tasks + +Just name your admin tasks as functions. For example, this will install AutoCAD LT 2016 and all the patches: + +```python +def install_autocad(): + mh.mount_and_install(os.path.join(APP_ROOT, 'AutoCAD/Autodesk AutoCAD LT 2016 for Mac Installation.dmg'), + 'Install Autodesk AutoCAD LT 2016 for Mac.pkg') + for x in xrange(1, 4): + mh.mount_and_install(os.path.join(APP_ROOT, 'AutoCAD/AutoCADLT2016Update%d.dmg' % x), + 'AutoCADLT2016Update%d.pkg' % x) +``` + +Since this is all just pure Python, feel free to put your Linux admin tasks in there as well. For instance, to update all your Maxwell rendernodes: + +```python +def update_render_nodes(): + for n in range(1, 11): + installer = os.path.join(APP_ROOT, 'Maxwell/Maxwell_3.2.1.5') + subprocess.call('ssh', 'admin@render%d.example.com' % d, 'killall mxnetwork;' + installer) +``` + +The above Linux example assumes you've configured your nodes to mount the fileshare the same way as your Macs (under /Volumes). You can run it from your admin Mac. + + +### Making install.py more "modular" + +Sometimes you just want to run parts of `install.py`. By adding this snippet to the end of `install.py`: + + +```python +if __name__ == '__main__': + if len(sys.argv) > 1: + for x in sys.argv[1:]: + try: + locals()[x]() + except KeyError as e: + mh.log('Function not found: %s' % x) + + sys.exit(0) +``` + +... you can call your named admin tasks (functions) simply providing their names on the command line: + + +```bash +$ python install.py install_autocad update_render_nodes +``` + + ### system_profiler `machammer` includes `system_profiler` - a small wrapper around OS X's `system_profiler (1)` tool. It provides a simple API for accessing system profile information as well as caching to improve performance (especially when dealing with application profile data). diff --git a/machammer/__init__.py b/machammer/__init__.py index 645ecea..1ab391e 100644 --- a/machammer/__init__.py +++ b/machammer/__init__.py @@ -1,4 +1,4 @@ -__all__ = [''] +__all__ = ['functions', 'printers', 'system_profiler'] __title__ = 'machammer' __author__ = 'Filipp Lepalaan' __version__ = '0.1' diff --git a/machammer/functions.py b/machammer/functions.py index b7f13b1..d797b32 100644 --- a/machammer/functions.py +++ b/machammer/functions.py @@ -161,7 +161,14 @@ def is_desktop(): def mount_image(dmg): """Mount disk image and return path to mountpoint.""" - r = subprocess.check_output(['/usr/bin/hdiutil', 'mount', '-plist', '-nobrowse', dmg]) + p = subprocess.Popen(['/usr/bin/hdiutil', 'mount', '-plist', '-nobrowse', dmg], + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + stderr=subprocess.PIPE) + r, e = p.communicate(input=b'Q\nY\n') + + if e: + raise Exception(e) try: plist = plistlib.readPlistFromString(r) @@ -172,7 +179,7 @@ def mount_image(dmg): if p and os.path.exists(p): return p - raise ValueError('Failed to mount %s' % dmg) + raise Exception('Failed to mount %s' % dmg) def mount_and_install(dmg, pkg): diff --git a/machammer/tests.py b/machammer/tests.py deleted file mode 100755 index 6e9b28e..0000000 --- a/machammer/tests.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import logging -import subprocess -from unittest import main, skip, TestCase - -import functions as mh -import system_profiler - - -class SystemProfilerTestCase(TestCase): - def testSerialNumber(self): - sn = system_profiler.get('Hardware', 'serial_number') - self.assertTrue(len(sn) > 8) - - def testInvalidType(self): - with self.assertRaises(Exception): - system_profiler.SystemProfile('Whatever') - - def testKeys(self): - self.assertTrue(len(system_profiler.keys()) > 3) - - def testTypes(self): - self.assertIn('Hardware', system_profiler.types()) - - def testOsVersion(self): - """ - Check that the OS version we get from SP is contained - in the output of sw_vers - """ - build = subprocess.check_output(['sw_vers', '-buildVersion']).strip() - software = system_profiler.SystemProfile('Software') - self.assertIn(build, software.os_version) - - def testOsVersionShortcut(self): - build = subprocess.check_output(['sw_vers', '-buildVersion']).strip() - self.assertTrue(build in system_profiler.get('Software', 'os_version')) - - -class AppsTestCase(TestCase): - def setUp(self): - self.profile = system_profiler.SystemProfile('Applications') - - def testFindStickes(self): - results = self.profile.find('_name', 'Stickies') - self.assertTrue(len(results) > 0) - - def testStickiesVersion(self): - results = self.profile.find('_name', 'Stickies') - self.assertEquals(results[0]['version'], '10.0') - - def testFindApplications(self): - results = self.profile.find('path', '/Applications') - self.assertTrue(len(results) > 10) - - -class FunctionsTestCase(TestCase): - def setUp(self): - self.stickes = '/Applications/Stickies.app' - - def test_notification(self): - mh.display_notification('blaaa "lalala"') - - def test_add_login_item(self): - mh.add_login_item(self.stickes) - - def test_remove_login_item(self): - mh.remove_login_item(path=self.stickes) - - -if __name__ == '__main__': - logging.basicConfig(level=logging.DEBUG) - main() diff --git a/tests.py b/tests.py new file mode 100755 index 0000000..fcce82a --- /dev/null +++ b/tests.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import logging +import subprocess +from unittest import main, skip, TestCase + +import machammer.functions as mh +from machammer import system_profiler + + +class SystemProfilerTestCase(TestCase): + def testSerialNumber(self): + sn = system_profiler.get('Hardware', 'serial_number') + self.assertTrue(len(sn) > 8) + + def testInvalidType(self): + with self.assertRaises(Exception): + system_profiler.SystemProfile('Whatever') + + def testKeys(self): + self.assertTrue(len(system_profiler.keys()) > 3) + + def testTypes(self): + self.assertIn('Hardware', system_profiler.types()) + + def testOsVersion(self): + """ + Check that the OS version we get from SP is contained + in the output of sw_vers + """ + build = subprocess.check_output(['sw_vers', '-buildVersion']).strip() + software = system_profiler.SystemProfile('Software') + self.assertIn(build, software.os_version) + + def testOsVersionShortcut(self): + build = subprocess.check_output(['sw_vers', '-buildVersion']).strip() + self.assertTrue(build in system_profiler.get('Software', 'os_version')) + + +class AppsTestCase(TestCase): + def setUp(self): + self.profile = system_profiler.SystemProfile('Applications') + + def testFindStickes(self): + results = self.profile.find('_name', 'Stickies') + self.assertTrue(len(results) > 0) + + def testStickiesVersion(self): + results = self.profile.find('_name', 'Stickies') + self.assertEquals(results[0]['version'], '10.0') + + def testFindApplications(self): + results = self.profile.find('path', '/Applications') + self.assertTrue(len(results) > 10) + + +class FunctionsTestCase(TestCase): + def setUp(self): + self.stickes = '/Applications/Stickies.app' + + def test_notification(self): + mh.display_notification('blaaa "lalala"') + + def test_add_login_item(self): + mh.add_login_item(self.stickes) + + def test_remove_login_item(self): + mh.remove_login_item(path=self.stickes) + + def test_mount_image(self): + p = mh.mount_image('/Users/filipp/Downloads/AdobeFlashPlayer_22au_a_install.dmg') + self.assertEquals(p, '/Volumes/Adobe Flash Player Installer') + + +if __name__ == '__main__': + logging.basicConfig(level=logging.DEBUG) + main() -- cgit v1.2.3