2013-04-09 09:05:55 +04:00
# Copyright (c) 2013 AnsibleWorks, Inc.
2013-03-29 09:02:07 +04:00
#
2013-04-09 09:05:55 +04:00
# This file is part of Ansible Commander.
#
2013-03-29 09:02:07 +04:00
# Ansible Commander is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
2013-04-09 09:05:55 +04:00
# the Free Software Foundation, version 3 of the License.
2013-03-29 09:02:07 +04:00
#
# Ansible Commander is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
2013-04-09 09:05:55 +04:00
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2013-03-29 09:02:07 +04:00
# GNU General Public License for more details.
2013-04-09 09:05:55 +04:00
#
2013-03-29 09:02:07 +04:00
# You should have received a copy of the GNU General Public License
2013-04-09 09:05:55 +04:00
# along with Ansible Commander. If not, see <http://www.gnu.org/licenses/>.
2013-03-29 09:02:07 +04:00
import json
2013-03-31 19:40:13 +04:00
import os
2013-03-29 09:02:07 +04:00
import StringIO
import sys
2013-04-04 21:59:32 +04:00
import tempfile
2013-03-29 09:02:07 +04:00
from django . core . management import call_command
from django . core . management . base import CommandError
2013-04-04 21:59:32 +04:00
from django . utils . timezone import now
2013-03-29 09:02:07 +04:00
from lib . main . models import *
from lib . main . tests . base import BaseTest
2013-04-04 21:59:32 +04:00
__all__ = [ ' AcomInventoryTest ' , ' AcomCallbackEventTest ' ]
2013-03-29 09:02:07 +04:00
class BaseCommandTest ( BaseTest ) :
'''
Base class for tests that run management commands .
'''
2013-03-31 19:40:13 +04:00
def setUp ( self ) :
super ( BaseCommandTest , self ) . setUp ( )
self . _environ = dict ( os . environ . items ( ) )
2013-04-04 21:59:32 +04:00
self . _temp_files = [ ]
2013-03-31 19:40:13 +04:00
def tearDown ( self ) :
super ( BaseCommandTest , self ) . tearDown ( )
for k , v in self . _environ . items ( ) :
if os . environ . get ( k , None ) != v :
os . environ [ k ] = v
for k , v in os . environ . items ( ) :
if k not in self . _environ . keys ( ) :
del os . environ [ k ]
2013-04-04 21:59:32 +04:00
for tf in self . _temp_files :
if os . path . exists ( tf ) :
os . remove ( tf )
2013-03-31 19:40:13 +04:00
2013-03-29 09:02:07 +04:00
def run_command ( self , name , * args , * * options ) :
'''
Run a management command and capture its stdout / stderr along with any
exceptions .
'''
2013-04-04 21:59:32 +04:00
stdin_fileobj = options . pop ( ' stdin_fileobj ' , None )
2013-03-29 09:02:07 +04:00
options . setdefault ( ' verbosity ' , 1 )
options . setdefault ( ' interactive ' , False )
2013-04-04 21:59:32 +04:00
original_stdin = sys . stdin
2013-03-29 09:02:07 +04:00
original_stdout = sys . stdout
original_stderr = sys . stderr
2013-04-04 21:59:32 +04:00
if stdin_fileobj :
sys . stdin = stdin_fileobj
2013-03-29 09:02:07 +04:00
sys . stdout = StringIO . StringIO ( )
sys . stderr = StringIO . StringIO ( )
result = None
try :
result = call_command ( name , * args , * * options )
except Exception , e :
result = e
except SystemExit , e :
result = e
finally :
captured_stdout = sys . stdout . getvalue ( )
captured_stderr = sys . stderr . getvalue ( )
2013-04-04 21:59:32 +04:00
sys . stdin = original_stdin
2013-03-29 09:02:07 +04:00
sys . stdout = original_stdout
sys . stderr = original_stderr
return result , captured_stdout , captured_stderr
class AcomInventoryTest ( BaseCommandTest ) :
'''
Test cases for acom_inventory management command .
'''
def setUp ( self ) :
2013-03-29 10:36:11 +04:00
super ( AcomInventoryTest , self ) . setUp ( )
self . setup_users ( )
self . organizations = self . make_organizations ( self . super_django_user , 2 )
self . projects = self . make_projects ( self . normal_django_user , 2 )
self . organizations [ 0 ] . projects . add ( self . projects [ 1 ] )
self . organizations [ 1 ] . projects . add ( self . projects [ 0 ] )
self . inventories = [ ]
self . hosts = [ ]
self . groups = [ ]
for n , organization in enumerate ( self . organizations ) :
inventory = Inventory . objects . create ( name = ' inventory- %d ' % n ,
description = ' description for inventory %d ' % n ,
organization = organization )
self . inventories . append ( inventory )
hosts = [ ]
for x in xrange ( 10 ) :
2013-03-31 19:40:13 +04:00
if n > 0 :
variable_data = VariableData . objects . create ( data = json . dumps ( { ' ho ' : ' hum- %d ' % x } ) )
else :
variable_data = None
host = inventory . hosts . create ( name = ' host- %02d - %02d .example.com ' % ( n , x ) ,
inventory = inventory ,
variable_data = variable_data )
2013-03-29 10:36:11 +04:00
hosts . append ( host )
self . hosts . extend ( hosts )
groups = [ ]
for x in xrange ( 5 ) :
2013-03-31 19:40:13 +04:00
if n > 0 :
variable_data = VariableData . objects . create ( data = json . dumps ( { ' gee ' : ' whiz- %d ' % x } ) )
else :
variable_data = None
2013-03-29 10:36:11 +04:00
group = inventory . groups . create ( name = ' group- %d ' % x ,
2013-03-31 19:40:13 +04:00
inventory = inventory ,
variable_data = variable_data )
2013-03-29 10:36:11 +04:00
groups . append ( group )
group . hosts . add ( hosts [ x ] )
group . hosts . add ( hosts [ x + 5 ] )
2013-03-31 19:40:13 +04:00
if n > 0 and x == 4 :
group . parents . add ( groups [ 3 ] )
2013-03-29 10:36:11 +04:00
self . groups . extend ( groups )
2013-03-29 09:02:07 +04:00
def test_without_inventory_id ( self ) :
result , stdout , stderr = self . run_command ( ' acom_inventory ' , list = True )
self . assertTrue ( isinstance ( result , CommandError ) )
self . assertEqual ( json . loads ( stdout ) , { } )
2013-03-31 19:40:13 +04:00
result , stdout , stderr = self . run_command ( ' acom_inventory ' ,
host = self . hosts [ 0 ] )
self . assertTrue ( isinstance ( result , CommandError ) )
self . assertEqual ( json . loads ( stdout ) , { } )
2013-03-29 09:02:07 +04:00
2013-03-31 19:40:13 +04:00
def test_list_with_inventory_id_as_argument ( self ) :
2013-03-29 10:36:11 +04:00
inventory = self . inventories [ 0 ]
2013-03-29 09:02:07 +04:00
result , stdout , stderr = self . run_command ( ' acom_inventory ' , list = True ,
2013-04-18 02:59:21 +04:00
inventory_id = inventory . pk )
2013-03-29 10:36:11 +04:00
self . assertEqual ( result , None )
data = json . loads ( stdout )
self . assertEqual ( set ( data . keys ( ) ) ,
set ( inventory . groups . values_list ( ' name ' , flat = True ) ) )
2013-03-31 19:40:13 +04:00
# Groups for this inventory should only have hosts, and no group
# variable data or parent/child relationships.
for k , v in data . items ( ) :
self . assertTrue ( isinstance ( v , ( list , tuple ) ) )
group = inventory . groups . get ( name = k )
self . assertEqual ( set ( v ) ,
set ( group . hosts . values_list ( ' name ' , flat = True ) ) )
2013-04-02 22:53:52 +04:00
# Command line argument for inventory ID should take precedence over
# environment variable.
inventory_pks = set ( map ( lambda x : x . pk , self . inventories ) )
invalid_id = [ x for x in xrange ( 9999 ) if x not in inventory_pks ] [ 0 ]
os . environ [ ' ACOM_INVENTORY_ID ' ] = str ( invalid_id )
result , stdout , stderr = self . run_command ( ' acom_inventory ' , list = True ,
2013-04-18 02:59:21 +04:00
inventory_id = inventory . pk )
2013-04-02 22:53:52 +04:00
self . assertEqual ( result , None )
data = json . loads ( stdout )
2013-03-31 19:40:13 +04:00
def test_list_with_inventory_id_in_environment ( self ) :
inventory = self . inventories [ 1 ]
os . environ [ ' ACOM_INVENTORY_ID ' ] = str ( inventory . pk )
result , stdout , stderr = self . run_command ( ' acom_inventory ' , list = True )
self . assertEqual ( result , None )
data = json . loads ( stdout )
self . assertEqual ( set ( data . keys ( ) ) ,
set ( inventory . groups . values_list ( ' name ' , flat = True ) ) )
# Groups for this inventory should have hosts, variable data, and one
# parent/child relationship.
for k , v in data . items ( ) :
self . assertTrue ( isinstance ( v , dict ) )
group = inventory . groups . get ( name = k )
self . assertEqual ( set ( v . get ( ' hosts ' , [ ] ) ) ,
set ( group . hosts . values_list ( ' name ' , flat = True ) ) )
if group . variable_data :
self . assertEqual ( v . get ( ' vars ' , { } ) ,
json . loads ( group . variable_data . data ) )
if k == ' group-3 ' :
self . assertEqual ( set ( v . get ( ' children ' , [ ] ) ) ,
set ( group . children . values_list ( ' name ' , flat = True ) ) )
else :
self . assertFalse ( ' children ' in v )
def test_valid_host ( self ) :
# Host without variable data.
inventory = self . inventories [ 0 ]
host = inventory . hosts . all ( ) [ 2 ]
os . environ [ ' ACOM_INVENTORY_ID ' ] = str ( inventory . pk )
result , stdout , stderr = self . run_command ( ' acom_inventory ' ,
host = host . name )
self . assertEqual ( result , None )
data = json . loads ( stdout )
self . assertEqual ( data , { } )
# Host with variable data.
inventory = self . inventories [ 1 ]
host = inventory . hosts . all ( ) [ 3 ]
os . environ [ ' ACOM_INVENTORY_ID ' ] = str ( inventory . pk )
result , stdout , stderr = self . run_command ( ' acom_inventory ' ,
host = host . name )
self . assertEqual ( result , None )
data = json . loads ( stdout )
self . assertEqual ( data , json . loads ( host . variable_data . data ) )
2013-03-29 09:02:07 +04:00
2013-03-31 19:40:13 +04:00
def test_invalid_host ( self ) :
# Valid host, but not part of the specified inventory.
inventory = self . inventories [ 0 ]
host = Host . objects . exclude ( inventory = inventory ) [ 0 ]
os . environ [ ' ACOM_INVENTORY_ID ' ] = str ( inventory . pk )
result , stdout , stderr = self . run_command ( ' acom_inventory ' ,
host = host . name )
self . assertTrue ( isinstance ( result , CommandError ) )
self . assertEqual ( json . loads ( stdout ) , { } )
# Invalid hostname not in database.
result , stdout , stderr = self . run_command ( ' acom_inventory ' ,
host = ' invalid.example.com ' )
self . assertTrue ( isinstance ( result , CommandError ) )
self . assertEqual ( json . loads ( stdout ) , { } )
2013-03-29 09:02:07 +04:00
def test_with_invalid_inventory_id ( self ) :
2013-03-31 19:40:13 +04:00
inventory_pks = set ( map ( lambda x : x . pk , self . inventories ) )
invalid_id = [ x for x in xrange ( 9999 ) if x not in inventory_pks ] [ 0 ]
os . environ [ ' ACOM_INVENTORY_ID ' ] = str ( invalid_id )
result , stdout , stderr = self . run_command ( ' acom_inventory ' , list = True )
self . assertTrue ( isinstance ( result , CommandError ) )
self . assertEqual ( json . loads ( stdout ) , { } )
os . environ [ ' ACOM_INVENTORY_ID ' ] = ' not_an_int '
result , stdout , stderr = self . run_command ( ' acom_inventory ' , list = True )
self . assertTrue ( isinstance ( result , CommandError ) )
self . assertEqual ( json . loads ( stdout ) , { } )
os . environ [ ' ACOM_INVENTORY_ID ' ] = str ( invalid_id )
result , stdout , stderr = self . run_command ( ' acom_inventory ' ,
host = self . hosts [ 1 ] )
self . assertTrue ( isinstance ( result , CommandError ) )
self . assertEqual ( json . loads ( stdout ) , { } )
os . environ [ ' ACOM_INVENTORY_ID ' ] = ' not_an_int '
result , stdout , stderr = self . run_command ( ' acom_inventory ' ,
host = self . hosts [ 2 ] )
self . assertTrue ( isinstance ( result , CommandError ) )
self . assertEqual ( json . loads ( stdout ) , { } )
def test_without_list_or_host_argument ( self ) :
inventory = self . inventories [ 0 ]
os . environ [ ' ACOM_INVENTORY_ID ' ] = str ( inventory . pk )
result , stdout , stderr = self . run_command ( ' acom_inventory ' )
self . assertTrue ( isinstance ( result , CommandError ) )
self . assertEqual ( json . loads ( stdout ) , { } )
2013-03-29 09:02:07 +04:00
2013-03-31 19:40:13 +04:00
def test_with_both_list_and_host_arguments ( self ) :
inventory = self . inventories [ 0 ]
os . environ [ ' ACOM_INVENTORY_ID ' ] = str ( inventory . pk )
result , stdout , stderr = self . run_command ( ' acom_inventory ' , list = True ,
host = ' blah ' )
self . assertTrue ( isinstance ( result , CommandError ) )
self . assertEqual ( json . loads ( stdout ) , { } )
2013-04-04 21:59:32 +04:00
class AcomCallbackEventTest ( BaseCommandTest ) :
'''
Test cases for acom_callback_event management command .
'''
def setUp ( self ) :
super ( AcomCallbackEventTest , self ) . setUp ( )
self . setup_users ( )
self . organization = self . make_organizations ( self . super_django_user , 1 ) [ 0 ]
self . project = self . make_projects ( self . normal_django_user , 1 ) [ 0 ]
self . organization . projects . add ( self . project )
self . inventory = Inventory . objects . create ( name = ' test-inventory ' ,
organization = self . organization )
self . host = self . inventory . hosts . create ( name = ' host.example.com ' ,
inventory = self . inventory )
self . group = self . inventory . groups . create ( name = ' test-group ' ,
inventory = self . inventory )
self . group . hosts . add ( self . host )
2013-04-18 02:59:21 +04:00
self . job = Job . objects . create ( name = ' job- %s ' % now ( ) . isoformat ( ) ,
inventory = self . inventory ,
project = self . project )
2013-04-04 21:59:32 +04:00
self . valid_kwargs = {
2013-04-18 02:59:21 +04:00
' job_id ' : self . job . id ,
2013-04-04 21:59:32 +04:00
' event_type ' : ' playbook_on_start ' ,
' event_data_json ' : json . dumps ( { ' test_event_data ' : [ 2 , 4 , 6 ] } ) ,
}
2013-04-18 02:59:21 +04:00
def test_with_job_status_not_running ( self ) :
# Events can only be added when the job is running.
self . assertEqual ( self . job . status , ' pending ' )
2013-04-04 21:59:32 +04:00
result , stdout , stderr = self . run_command ( ' acom_callback_event ' ,
* * self . valid_kwargs )
self . assertTrue ( isinstance ( result , CommandError ) )
self . assertTrue ( ' unable to add event ' in str ( result ) . lower ( ) )
2013-04-18 02:59:21 +04:00
self . job . status = ' successful '
self . job . save ( )
2013-04-04 21:59:32 +04:00
result , stdout , stderr = self . run_command ( ' acom_callback_event ' ,
* * self . valid_kwargs )
self . assertTrue ( isinstance ( result , CommandError ) )
self . assertTrue ( ' unable to add event ' in str ( result ) . lower ( ) )
2013-04-18 02:59:21 +04:00
self . job . status = ' failed '
self . job . save ( )
2013-04-04 21:59:32 +04:00
result , stdout , stderr = self . run_command ( ' acom_callback_event ' ,
* * self . valid_kwargs )
self . assertTrue ( isinstance ( result , CommandError ) )
self . assertTrue ( ' unable to add event ' in str ( result ) . lower ( ) )
def test_with_invalid_args ( self ) :
2013-04-18 02:59:21 +04:00
self . job . status = ' running '
self . job . save ( )
2013-04-04 21:59:32 +04:00
# Event type not given.
kwargs = dict ( self . valid_kwargs . items ( ) )
kwargs . pop ( ' event_type ' )
result , stdout , stderr = self . run_command ( ' acom_callback_event ' , * * kwargs )
self . assertTrue ( isinstance ( result , CommandError ) )
self . assertTrue ( ' no event specified ' in str ( result ) . lower ( ) )
# Invalid event type.
kwargs = dict ( self . valid_kwargs . items ( ) )
kwargs [ ' event_type ' ] = ' invalid_event_type '
result , stdout , stderr = self . run_command ( ' acom_callback_event ' , * * kwargs )
self . assertTrue ( isinstance ( result , CommandError ) )
self . assertTrue ( ' unsupported event ' in str ( result ) . lower ( ) )
# Neither file or data specified.
kwargs = dict ( self . valid_kwargs . items ( ) )
kwargs . pop ( ' event_data_json ' )
result , stdout , stderr = self . run_command ( ' acom_callback_event ' , * * kwargs )
self . assertTrue ( isinstance ( result , CommandError ) )
self . assertTrue ( ' either --file or --data ' in str ( result ) . lower ( ) )
2013-04-18 02:59:21 +04:00
# Non-integer job ID.
2013-04-04 21:59:32 +04:00
kwargs = dict ( self . valid_kwargs . items ( ) )
2013-04-18 02:59:21 +04:00
kwargs [ ' job_id ' ] = ' foo '
2013-04-04 21:59:32 +04:00
result , stdout , stderr = self . run_command ( ' acom_callback_event ' , * * kwargs )
self . assertTrue ( isinstance ( result , CommandError ) )
self . assertTrue ( ' id must be an integer ' in str ( result ) . lower ( ) )
2013-04-18 02:59:21 +04:00
# No job ID.
2013-04-04 21:59:32 +04:00
kwargs = dict ( self . valid_kwargs . items ( ) )
2013-04-18 02:59:21 +04:00
kwargs . pop ( ' job_id ' )
2013-04-04 21:59:32 +04:00
result , stdout , stderr = self . run_command ( ' acom_callback_event ' , * * kwargs )
self . assertTrue ( isinstance ( result , CommandError ) )
2013-04-18 02:59:21 +04:00
self . assertTrue ( ' no job id ' in str ( result ) . lower ( ) )
# Invalid job ID.
2013-04-04 21:59:32 +04:00
kwargs = dict ( self . valid_kwargs . items ( ) )
2013-04-18 02:59:21 +04:00
kwargs [ ' job_id ' ] = 9999
2013-04-04 21:59:32 +04:00
result , stdout , stderr = self . run_command ( ' acom_callback_event ' , * * kwargs )
self . assertTrue ( isinstance ( result , CommandError ) )
self . assertTrue ( ' not found ' in str ( result ) . lower ( ) )
# Invalid inline JSON data.
kwargs = dict ( self . valid_kwargs . items ( ) )
kwargs [ ' event_data_json ' ] = ' invalid json '
result , stdout , stderr = self . run_command ( ' acom_callback_event ' , * * kwargs )
self . assertTrue ( isinstance ( result , CommandError ) )
self . assertTrue ( ' error parsing json ' in str ( result ) . lower ( ) )
# Invalid file specified.
kwargs = dict ( self . valid_kwargs . items ( ) )
kwargs . pop ( ' event_data_json ' )
h , tf = tempfile . mkstemp ( )
os . close ( h )
os . remove ( tf )
kwargs [ ' event_data_file ' ] = ' %s .json ' % tf
result , stdout , stderr = self . run_command ( ' acom_callback_event ' , * * kwargs )
self . assertTrue ( isinstance ( result , CommandError ) )
self . assertTrue ( ' reading from ' in str ( result ) . lower ( ) )
def test_with_valid_args ( self ) :
2013-04-18 02:59:21 +04:00
self . job . status = ' running '
self . job . save ( )
2013-04-04 21:59:32 +04:00
# Default valid args.
kwargs = dict ( self . valid_kwargs . items ( ) )
result , stdout , stderr = self . run_command ( ' acom_callback_event ' , * * kwargs )
self . assertEqual ( result , None )
2013-04-18 02:59:21 +04:00
self . assertEqual ( self . job . job_events . count ( ) , 1 )
# Pass job ID in environment instead.
2013-04-04 21:59:32 +04:00
kwargs = dict ( self . valid_kwargs . items ( ) )
2013-04-18 02:59:21 +04:00
kwargs . pop ( ' job_id ' )
os . environ [ ' ACOM_JOB_ID ' ] = str ( self . job . id )
2013-04-04 21:59:32 +04:00
result , stdout , stderr = self . run_command ( ' acom_callback_event ' , * * kwargs )
self . assertEqual ( result , None )
2013-04-18 02:59:21 +04:00
self . assertEqual ( self . job . job_events . count ( ) , 2 )
os . environ . pop ( ' ACOM_JOB_ID ' , None )
2013-04-04 21:59:32 +04:00
# Test with JSON data in a file instead.
kwargs = dict ( self . valid_kwargs . items ( ) )
kwargs . pop ( ' event_data_json ' )
h , tf = tempfile . mkstemp ( suffix = ' .json ' )
self . _temp_files . append ( tf )
f = os . fdopen ( h , ' w ' )
json . dump ( { ' some_event_data ' : [ 1 , 2 , 3 ] } , f )
f . close ( )
kwargs [ ' event_data_file ' ] = tf
result , stdout , stderr = self . run_command ( ' acom_callback_event ' , * * kwargs )
self . assertEqual ( result , None )
2013-04-18 02:59:21 +04:00
self . assertEqual ( self . job . job_events . count ( ) , 3 )
2013-04-04 21:59:32 +04:00
# Test with JSON data from stdin.
kwargs = dict ( self . valid_kwargs . items ( ) )
kwargs . pop ( ' event_data_json ' )
kwargs [ ' event_data_file ' ] = ' - '
kwargs [ ' stdin_fileobj ' ] = StringIO . StringIO ( json . dumps ( { ' blah ' : ' bleep ' } ) )
result , stdout , stderr = self . run_command ( ' acom_callback_event ' , * * kwargs )
self . assertEqual ( result , None )
2013-04-18 02:59:21 +04:00
self . assertEqual ( self . job . job_events . count ( ) , 4 )