├── LICENSE ├── README.rst └── callback_plugins └── profile_tasks.py /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jharrod LaFon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | =============== 2 | ansible-profile 3 | =============== 4 | 5 | An Ansible plugin for timing tasks. 6 | 7 | 8 | Usage in Ansible 2.0 9 | ^^^^^^^^^^^^^^^^^^^^ 10 | 11 | This plugin is included in Ansible 2.0, and can be enabled by adding this line to `ansible.cfg` 12 | 13 | .. code-block:: bash 14 | 15 | callback_whitelist = profile_tasks 16 | 17 | Usage in Ansible 1.x 18 | ^^^^^^^^^^^^^^^^^^^^ 19 | 20 | Make a directory called `callback_plugins` next to your playbook and put `profile_tasks.py` inside of it. 21 | 22 | .. code-block:: bash 23 | 24 | mkdir callback_plugins 25 | cd callback_plugins 26 | wget https://raw.githubusercontent.com/jlafon/ansible-profile/master/callback_plugins/profile_tasks.py 27 | 28 | Now, run your playbook just as you normally would! 29 | 30 | .. code-block:: bash 31 | 32 | ansible 33 | 34 | PLAY RECAP ******************************************************************** 35 | really slow task | Download project packages-----------------------------11.61s 36 | security | Really slow security policies-----------------------------------7.03s 37 | common-base | Install core system dependencies-----------------------------3.62s 38 | common | Install pip-------------------------------------------------------3.60s 39 | common | Install boto------------------------------------------------------3.57s 40 | nginx | Install nginx------------------------------------------------------3.41s 41 | serf | Install system dependencies-----------------------------------------3.38s 42 | duo_security | Install Duo Unix SSH Integration----------------------------3.37s 43 | loggly | Install TLS version-----------------------------------------------3.36s 44 | 45 | If for some reason you want to disable this temporarily, set the 46 | environment variable `ANSIBLE_PROFILE_DISABLE` to any value (even an 47 | empty string). 48 | -------------------------------------------------------------------------------- /callback_plugins/profile_tasks.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import os 3 | import time 4 | from ansible.plugins.callback import CallbackBase 5 | 6 | 7 | class CallbackModule(CallbackBase): 8 | """ 9 | A plugin for timing tasks 10 | """ 11 | def __init__(self): 12 | super(CallbackModule, self).__init__() 13 | self.stats = {} 14 | self.current = None 15 | 16 | def playbook_on_task_start(self, name, is_conditional): 17 | """ 18 | Logs the start of each task 19 | """ 20 | 21 | if os.getenv("ANSIBLE_PROFILE_DISABLE") is not None: 22 | return 23 | 24 | if self.current is not None: 25 | # Record the running time of the last executed task 26 | self.stats[self.current] = time.time() - self.stats[self.current] 27 | 28 | # Record the start time of the current task 29 | self.current = name 30 | self.stats[self.current] = time.time() 31 | 32 | def playbook_on_stats(self, stats): 33 | """ 34 | Prints the timings 35 | """ 36 | 37 | if os.getenv("ANSIBLE_PROFILE_DISABLE") is not None: 38 | return 39 | 40 | # Record the timing of the very last task 41 | if self.current is not None: 42 | self.stats[self.current] = time.time() - self.stats[self.current] 43 | 44 | # Sort the tasks by their running time 45 | results = sorted( 46 | self.stats.items(), 47 | key=lambda value: value[1], 48 | reverse=True, 49 | ) 50 | 51 | # Just keep the top 10 52 | results = results[:10] 53 | 54 | # Print the timings 55 | for name, elapsed in results: 56 | print( 57 | "{0:-<70}{1:->9}".format( 58 | '{0} '.format(name), 59 | ' {0:.02f}s'.format(elapsed), 60 | ) 61 | ) 62 | 63 | total_seconds = sum([x[1] for x in self.stats.items()]) 64 | print("\nPlaybook finished: {0}, {1} total tasks. {2} elapsed. \n".format( 65 | time.asctime(), 66 | len(self.stats.items()), 67 | datetime.timedelta(seconds=(int(total_seconds))) 68 | ) 69 | ) 70 | --------------------------------------------------------------------------------