signature:
def list ( self , all_pages = False , ** kwargs ) :
    """Return a list of objects .
 If one or more filters are provided through keyword arguments , filter the results accordingly .
 If no filters are provided , return all results .
 = = = = = API DOCS = = = = =
 Retrieve a list of objects .
 : param all _ pages : Flag that if set , collect all pages of content from the API when returning results .
 : type all _ pages : bool
 : param page : The page to show . Ignored if all _ pages is set .
 : type page : int
 : param query : Contains 2 - tuples used as query parameters to filter resulting resource objects .
 : type query : list
 : param ` * * kwargs ` : Keyword arguments list of available fields used for searching resource objects .
 : returns : A JSON object containing details of all resource objects returned by Tower backend .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation:
# TODO : Move to a field callback method to make it generic
# If multiple statuses where given , add OR queries for each of them
if kwargs . get ( 'status' , None ) and ',' in kwargs [ 'status' ] :
    all_status = kwargs . pop ( 'status' ) . strip ( ',' ) . split ( ',' )
    queries = list ( kwargs . pop ( 'query' , ( ) ) )
    for status in all_status :
        if status in STATUS_CHOICES :
            queries . append ( ( 'or__status' , status ) )
        else :
            raise exc . TowerCLIError ( 'This status does not exist: {}' . format ( status ) )
    kwargs [ 'query' ] = tuple ( queries )
# If the ` all _ pages ` flag is set , then ignore any page that might also be sent .
if all_pages :
    kwargs . pop ( 'page' , None )
    kwargs . pop ( 'page_size' , None )
# Get the response .
debug . log ( 'Getting records.' , header = 'details' )
response = self . read ( ** kwargs )
# Convert next and previous to int
self . _convert_pagenum ( response )
# If we were asked for all pages , keep retrieving pages until we have them all .
if all_pages and response [ 'next' ] :
    cursor = copy ( response )
    while cursor [ 'next' ] :
        cursor = self . read ( ** dict ( kwargs , page = cursor [ 'next' ] ) )
        self . _convert_pagenum ( cursor )
        response [ 'results' ] += cursor [ 'results' ]
        response [ 'count' ] += cursor [ 'count' ]
    response [ 'next' ] = None
# Done ; return the response
return response


signature:
def _disassoc ( self , url_fragment , me , other ) :
    """Disassociate the ` other ` record from the ` me ` record ."""

implementation:
# Get the endpoint for foreign records within this object .
url = self . endpoint + '%d/%s/' % ( me , url_fragment )
# Attempt to determine whether the other record already is absent , for the " changed " moniker .
r = client . get ( url , params = { 'id' : other } ) . json ( )
if r [ 'count' ] == 0 :
    return { 'changed' : False }
# Send a request removing the foreign record from this one .
r = client . post ( url , data = { 'disassociate' : True , 'id' : other } )
return { 'changed' : True }


signature:
def copy ( self , pk = None , new_name = None , ** kwargs ) :
    """Copy an object .
 Only the ID is used for the lookup . All provided fields are used to override the old data from the
 copied resource .
 = = = = = API DOCS = = = = =
 Copy an object .
 : param pk : Primary key of the resource object to be copied
 : param new _ name : The new name to give the resource if deep copying via the API
 : type pk : int
 : param ` * * kwargs ` : Keyword arguments of fields whose given value will override the original value .
 : returns : loaded JSON of the copied new resource object .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation:
orig = self . read ( pk , fail_on_no_results = True , fail_on_multiple_results = True )
orig = orig [ 'results' ] [ 0 ]
# Remove default values ( anything where the value is None ) .
self . _pop_none ( kwargs )
newresource = copy ( orig )
newresource . pop ( 'id' )
basename = newresource [ 'name' ] . split ( '@' , 1 ) [ 0 ] . strip ( )
# Modify data to fit the call pattern of the tower - cli method
for field in self . fields :
    if field . multiple and field . name in newresource :
        newresource [ field . name ] = ( newresource . get ( field . name ) , )
if new_name is None : # copy client - side , the old mechanism
    newresource [ 'name' ] = "%s @ %s" % ( basename , time . strftime ( '%X' ) )
    newresource . update ( kwargs )
    return self . write ( create_on_missing = True , fail_on_found = True , ** newresource )
else : # copy server - side , the new mechanism
    if kwargs :
        raise exc . TowerCLIError ( 'Cannot override {} and also use --new-name.' . format ( kwargs . keys ( ) ) )
    copy_endpoint = '{}/{}/copy/' . format ( self . endpoint . strip ( '/' ) , pk )
    return client . post ( copy_endpoint , data = { 'name' : new_name } ) . json ( )


signature:
def modify ( self , pk = None , create_on_missing = False , ** kwargs ) :
    """Modify an already existing object .
 Fields in the resource ' s ` identity ` tuple can be used in lieu of a primary key for a lookup ; in such a case ,
 only other fields are written .
 To modify unique fields , you must use the primary key for the lookup .
 = = = = = API DOCS = = = = =
 Modify an already existing object .
 : param pk : Primary key of the resource to be modified .
 : type pk : int
 : param create _ on _ missing : Flag that if set , a new object is created if ` ` pk ` ` is not set and objects
 matching the appropriate unique criteria is not found .
 : type create _ on _ missing : bool
 : param ` * * kwargs ` : Keyword arguments which , all together , will be used as PATCH body to modify the
 resource object . if ` ` pk ` ` is not set , key - value pairs of ` ` * * kwargs ` ` which are
 also in resource ' s identity will be used to lookup existing reosource .
 : returns : A dictionary combining the JSON output of the modified resource , as well as two extra fields :
 " changed " , a flag indicating if the resource is successfully updated ; " id " , an integer which
 is the primary key of the updated object .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation: return self . write ( pk , create_on_missing = create_on_missing , force_on_exists = True , ** kwargs )


signature:
def last_job_data ( self , pk = None , ** kwargs ) :
    """Internal utility function for Unified Job Templates . Returns data about the last job run off of that UJT"""

implementation:
ujt = self . get ( pk , include_debug_header = True , ** kwargs )
# Determine the appropriate inventory source update .
if 'current_update' in ujt [ 'related' ] :
    debug . log ( 'A current job; retrieving it.' , header = 'details' )
    return client . get ( ujt [ 'related' ] [ 'current_update' ] [ 7 : ] ) . json ( )
elif ujt [ 'related' ] . get ( 'last_update' , None ) :
    debug . log ( 'No current job or update exists; retrieving the most recent.' , header = 'details' )
    return client . get ( ujt [ 'related' ] [ 'last_update' ] [ 7 : ] ) . json ( )
else :
    raise exc . NotFound ( 'No related jobs or updates exist.' )


signature:
def lookup_stdout ( self , pk = None , start_line = None , end_line = None , full = True ) :
    """Internal utility function to return standard out . Requires the pk of a unified job ."""

implementation:
stdout_url = '%s%s/stdout/' % ( self . unified_job_type , pk )
payload = { 'format' : 'json' , 'content_encoding' : 'base64' , 'content_format' : 'ansi' }
if start_line :
    payload [ 'start_line' ] = start_line
if end_line :
    payload [ 'end_line' ] = end_line
debug . log ( 'Requesting a copy of job standard output' , header = 'details' )
resp = client . get ( stdout_url , params = payload ) . json ( )
content = b64decode ( resp [ 'content' ] )
return content . decode ( 'utf-8' , 'replace' )


signature:
def stdout ( self , pk , start_line = None , end_line = None , outfile = sys . stdout , ** kwargs ) :
    """Print out the standard out of a unified job to the command line or output file .
 For Projects , print the standard out of most recent update .
 For Inventory Sources , print standard out of most recent sync .
 For Jobs , print the job ' s standard out .
 For Workflow Jobs , print a status table of its jobs .
 = = = = = API DOCS = = = = =
 Print out the standard out of a unified job to the command line or output file .
 For Projects , print the standard out of most recent update .
 For Inventory Sources , print standard out of most recent sync .
 For Jobs , print the job ' s standard out .
 For Workflow Jobs , print a status table of its jobs .
 : param pk : Primary key of the job resource object to be monitored .
 : type pk : int
 : param start _ line : Line at which to start printing job output
 : param end _ line : Line at which to end printing job output
 : param outfile : Alternative file than stdout to write job stdout to .
 : type outfile : file
 : param ` * * kwargs ` : Keyword arguments used to look up job resource object to monitor if ` ` pk ` ` is
 not provided .
 : returns : A dictionary containing changed = False
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation:
# resource is Unified Job Template
if self . unified_job_type != self . endpoint :
    unified_job = self . last_job_data ( pk , ** kwargs )
    pk = unified_job [ 'id' ]
# resource is Unified Job , but pk not given
elif not pk :
    unified_job = self . get ( ** kwargs )
    pk = unified_job [ 'id' ]
content = self . lookup_stdout ( pk , start_line , end_line )
opened = False
if isinstance ( outfile , six . string_types ) :
    outfile = open ( outfile , 'w' )
    opened = True
if len ( content ) > 0 :
    click . echo ( content , nl = 1 , file = outfile )
if opened :
    outfile . close ( )
return { "changed" : False }


signature:
def monitor ( self , pk , parent_pk = None , timeout = None , interval = 0.5 , outfile = sys . stdout , ** kwargs ) :
    """Stream the standard output from a job , project update , or inventory udpate .
 = = = = = API DOCS = = = = =
 Stream the standard output from a job run to stdout .
 : param pk : Primary key of the job resource object to be monitored .
 : type pk : int
 : param parent _ pk : Primary key of the unified job template resource object whose latest job run will be
 monitored if ` ` pk ` ` is not set .
 : type parent _ pk : int
 : param timeout : Number in seconds after which this method will time out .
 : type timeout : float
 : param interval : Polling interval to refresh content from Tower .
 : type interval : float
 : param outfile : Alternative file than stdout to write job stdout to .
 : type outfile : file
 : param ` * * kwargs ` : Keyword arguments used to look up job resource object to monitor if ` ` pk ` ` is
 not provided .
 : returns : A dictionary combining the JSON output of the finished job resource object , as well as
 two extra fields : " changed " , a flag indicating if the job resource object is finished
 as expected ; " id " , an integer which is the primary key of the job resource object being
 monitored .
 : rtype : dict
 : raises tower _ cli . exceptions . Timeout : When monitor time reaches time out .
 : raises tower _ cli . exceptions . JobFailure : When the job being monitored runs into failure .
 = = = = = API DOCS = = = = ="""

implementation:
# If we do not have the unified job info , infer it from parent
if pk is None :
    pk = self . last_job_data ( parent_pk , ** kwargs ) [ 'id' ]
job_endpoint = '%s%s/' % ( self . unified_job_type , pk )
# Pause until job is in running state
self . wait ( pk , exit_on = [ 'running' , 'successful' ] , outfile = outfile )
# Loop initialization
start = time . time ( )
start_line = 0
result = client . get ( job_endpoint ) . json ( )
click . echo ( '\033[0;91m------Starting Standard Out Stream------\033[0m' , nl = 2 , file = outfile )
# Poll the Ansible Tower instance for status and content , and print standard out to the out file
while not result [ 'failed' ] and result [ 'status' ] != 'successful' :
    result = client . get ( job_endpoint ) . json ( )
    # Put the process to sleep briefly .
    time . sleep ( interval )
    # Make request to get standard out
    content = self . lookup_stdout ( pk , start_line , full = False )
    # In the first moments of running the job , the standard out
    # may not be available yet
    if not content . startswith ( "Waiting for results" ) :
        line_count = len ( content . splitlines ( ) )
        start_line += line_count
        click . echo ( content , nl = 0 , file = outfile )
    if timeout and time . time ( ) - start > timeout :
        raise exc . Timeout ( 'Monitoring aborted due to timeout.' )
# Special final line for closure with workflow jobs
if self . endpoint == '/workflow_jobs/' :
    click . echo ( self . lookup_stdout ( pk , start_line , full = True ) , nl = 1 )
click . echo ( '\033[0;91m------End of Standard Out Stream--------\033[0m' , nl = 2 , file = outfile )
if result [ 'failed' ] :
    raise exc . JobFailure ( 'Job failed.' )
# Return the job ID and other response data
answer = OrderedDict ( ( ( 'changed' , True ) , ( 'id' , pk ) ) )
answer . update ( result )
# Make sure to return ID of resource and not update number relevant for project creation and update
if parent_pk :
    answer [ 'id' ] = parent_pk
else :
    answer [ 'id' ] = pk
return answer


signature:
def wait ( self , pk , parent_pk = None , min_interval = 1 , max_interval = 30 , timeout = None , outfile = sys . stdout , exit_on = [ 'successful' ] , ** kwargs ) :
    """Wait for a running job to finish . Blocks further input until the job completes ( whether successfully
 or unsuccessfully ) and a final status can be given .
 = = = = = API DOCS = = = = =
 Wait for a job resource object to enter certain status .
 : param pk : Primary key of the job resource object to wait .
 : type pk : int
 : param parent _ pk : Primary key of the unified job template resource object whose latest job run will be
 waited if ` ` pk ` ` is not set .
 : type parent _ pk : int
 : param timeout : Number in seconds after which this method will time out .
 : type timeout : float
 : param min _ interval : Minimum polling interval to request an update from Tower .
 : type min _ interval : float
 : param max _ interval : Maximum polling interval to request an update from Tower .
 : type max _ interval : float
 : param outfile : Alternative file than stdout to write job status updates on .
 : type outfile : file
 : param exit _ on : Job resource object statuses to wait on .
 : type exit _ on : array
 : param ` * * kwargs ` : Keyword arguments used to look up job resource object to wait if ` ` pk ` ` is
 not provided .
 : returns : A dictionary combining the JSON output of the status - changed job resource object , as well
 as two extra fields : " changed " , a flag indicating if the job resource object is status - changed
 as expected ; " id " , an integer which is the primary key of the job resource object being
 status - changed .
 : rtype : dict
 : raises tower _ cli . exceptions . Timeout : When wait time reaches time out .
 : raises tower _ cli . exceptions . JobFailure : When the job being waited on runs into failure .
 = = = = = API DOCS = = = = ="""

implementation:
# If we do not have the unified job info , infer it from parent
if pk is None :
    pk = self . last_job_data ( parent_pk , ** kwargs ) [ 'id' ]
job_endpoint = '%s%s/' % ( self . unified_job_type , pk )
dots = itertools . cycle ( [ 0 , 1 , 2 , 3 ] )
longest_string = 0
interval = min_interval
start = time . time ( )
# Poll the Ansible Tower instance for status , and print the status to the outfile ( usually standard out ) .
# Note that this is one of the few places where we use ` secho ` even though we ' re in a function that might
# theoretically be imported and run in Python . This seems fine ; outfile can be set to / dev / null and very
# much the normal use for this method should be CLI monitoring .
result = client . get ( job_endpoint ) . json ( )
last_poll = time . time ( )
timeout_check = 0
while result [ 'status' ] not in exit_on : # If the job has failed , we want to raise an Exception for that so we get a non - zero response .
    if result [ 'failed' ] :
        if is_tty ( outfile ) and not settings . verbose :
            secho ( '\r' + ' ' * longest_string + '\n' , file = outfile )
        raise exc . JobFailure ( 'Job failed.' )
    # Sanity check : Have we officially timed out ?
    # The timeout check is incremented below , so this is checking to see if we were timed out as of
    # the previous iteration . If we are timed out , abort .
    if timeout and timeout_check - start > timeout :
        raise exc . Timeout ( 'Monitoring aborted due to timeout.' )
    # If the outfile is a TTY , print the current status .
    output = '\rCurrent status: %s%s' % ( result [ 'status' ] , '.' * next ( dots ) )
    if longest_string > len ( output ) :
        output += ' ' * ( longest_string - len ( output ) )
    else :
        longest_string = len ( output )
    if is_tty ( outfile ) and not settings . verbose :
        secho ( output , nl = False , file = outfile )
    # Put the process to sleep briefly .
    time . sleep ( 0.2 )
    # Sanity check : Have we reached our timeout ?
    # If we ' re about to time out , then we need to ensure that we do one last check .
    # Note that the actual timeout will be performed at the start of the * * next * * iteration ,
    # so there ' s a chance for the job ' s completion to be noted first .
    timeout_check = time . time ( )
    if timeout and timeout_check - start > timeout :
        last_poll -= interval
    # If enough time has elapsed , ask the server for a new status .
    # Note that this doesn ' t actually do a status check every single time ; we want the " spinner " to
    # spin even if we ' re not actively doing a check .
    # So , what happens is that we are " counting down " ( actually up ) to the next time that we intend
    # to do a check , and once that time hits , we do the status check as part of the normal cycle .
    if time . time ( ) - last_poll > interval :
        result = client . get ( job_endpoint ) . json ( )
        last_poll = time . time ( )
        interval = min ( interval * 1.5 , max_interval )
        # If the outfile is * not * a TTY , print a status update when and only when we make an actual
        # check to job status .
        if not is_tty ( outfile ) or settings . verbose :
            click . echo ( 'Current status: %s' % result [ 'status' ] , file = outfile )
    # Wipe out the previous output
    if is_tty ( outfile ) and not settings . verbose :
        secho ( '\r' + ' ' * longest_string , file = outfile , nl = False )
        secho ( '\r' , file = outfile , nl = False )
# Return the job ID and other response data
answer = OrderedDict ( ( ( 'changed' , True ) , ( 'id' , pk ) ) )
answer . update ( result )
# Make sure to return ID of resource and not update number relevant for project creation and update
if parent_pk :
    answer [ 'id' ] = parent_pk
else :
    answer [ 'id' ] = pk
return answer


signature:
def status ( self , pk = None , detail = False , ** kwargs ) :
    """Print the current job status . This is used to check a running job . You can look up the job with
 the same parameters used for a get request .
 = = = = = API DOCS = = = = =
 Retrieve the current job status .
 : param pk : Primary key of the resource to retrieve status from .
 : type pk : int
 : param detail : Flag that if set , return the full JSON of the job resource rather than a status summary .
 : type detail : bool
 : param ` * * kwargs ` : Keyword arguments used to look up resource object to retrieve status from if ` ` pk ` `
 is not provided .
 : returns : full loaded JSON of the specified unified job if ` ` detail ` ` flag is on ; trimed JSON containing
 only " elapsed " , " failed " and " status " fields of the unified job if ` ` detail ` ` flag is off .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation:
# Remove default values ( anything where the value is None ) .
self . _pop_none ( kwargs )
# Search for the record if pk not given
if not pk :
    job = self . get ( include_debug_header = True , ** kwargs )
# Get the job from Ansible Tower if pk given
else :
    debug . log ( 'Asking for job status.' , header = 'details' )
    finished_endpoint = '%s%s/' % ( self . endpoint , pk )
    job = client . get ( finished_endpoint ) . json ( )
# In most cases , we probably only want to know the status of the job and the amount of time elapsed .
# However , if we were asked for verbose information , provide it .
if detail :
    return job
# Print just the information we need .
return { 'elapsed' : job [ 'elapsed' ] , 'failed' : job [ 'failed' ] , 'status' : job [ 'status' ] , }


signature:
def cancel ( self , pk = None , fail_if_not_running = False , ** kwargs ) :
    """Cancel a currently running job .
 Fails with a non - zero exit status if the job cannot be canceled .
 You must provide either a pk or parameters in the job ' s identity .
 = = = = = API DOCS = = = = =
 Cancel a currently running job .
 : param pk : Primary key of the job resource to restart .
 : type pk : int
 : param fail _ if _ not _ running : Flag that if set , raise exception if the job resource cannot be canceled .
 : type fail _ if _ not _ running : bool
 : param ` * * kwargs ` : Keyword arguments used to look up job resource object to restart if ` ` pk ` ` is not
 provided .
 : returns : A dictionary of two keys : " status " , which is " canceled " , and " changed " , which indicates if
 the job resource has been successfully canceled .
 : rtype : dict
 : raises tower _ cli . exceptions . TowerCLIError : When the job resource cannot be canceled and
 ` ` fail _ if _ not _ running ` ` flag is on .
 = = = = = API DOCS = = = = ="""

implementation:
# Search for the record if pk not given
if not pk :
    existing_data = self . get ( ** kwargs )
    pk = existing_data [ 'id' ]
cancel_endpoint = '%s%s/cancel/' % ( self . endpoint , pk )
# Attempt to cancel the job .
try :
    client . post ( cancel_endpoint )
    changed = True
except exc . MethodNotAllowed :
    changed = False
    if fail_if_not_running :
        raise exc . TowerCLIError ( 'Job not running.' )
# Return a success .
return { 'status' : 'canceled' , 'changed' : changed }


signature:
def relaunch ( self , pk = None , ** kwargs ) :
    """Relaunch a stopped job .
 Fails with a non - zero exit status if the job cannot be relaunched .
 You must provide either a pk or parameters in the job ' s identity .
 = = = = = API DOCS = = = = =
 Relaunch a stopped job resource .
 : param pk : Primary key of the job resource to relaunch .
 : type pk : int
 : param ` * * kwargs ` : Keyword arguments used to look up job resource object to relaunch if ` ` pk ` ` is not
 provided .
 : returns : A dictionary combining the JSON output of the relaunched job resource object , as well
 as an extra field " changed " , a flag indicating if the job resource object is status - changed
 as expected .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation:
# Search for the record if pk not given
if not pk :
    existing_data = self . get ( ** kwargs )
    pk = existing_data [ 'id' ]
relaunch_endpoint = '%s%s/relaunch/' % ( self . endpoint , pk )
data = { }
# Attempt to relaunch the job .
answer = { }
try :
    result = client . post ( relaunch_endpoint , data = data ) . json ( )
    if 'id' in result :
        answer . update ( result )
    answer [ 'changed' ] = True
except exc . MethodNotAllowed :
    answer [ 'changed' ] = False
# Return the answer .
return answer


signature:
def survey ( self , pk = None , ** kwargs ) :
    """Get the survey _ spec for the job template .
 To write a survey , use the modify command with the - - survey - spec parameter .
 = = = = = API DOCS = = = = =
 Get the survey specification of a resource object .
 : param pk : Primary key of the resource to retrieve survey from . Tower CLI will only attempt to
 read * that * object if ` ` pk ` ` is provided ( not ` ` None ` ` ) .
 : type pk : int
 : param ` * * kwargs ` : Keyword arguments used to look up resource object to retrieve survey if ` ` pk ` `
 is not provided .
 : returns : loaded JSON of the retrieved survey specification of the resource object .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation:
job_template = self . get ( pk = pk , ** kwargs )
if settings . format == 'human' :
    settings . format = 'json'
return client . get ( self . _survey_endpoint ( job_template [ 'id' ] ) ) . json ( )


signature:
def batch_update ( self , pk = None , ** kwargs ) :
    """Update all related inventory sources of the given inventory .
 Note global option - - format is not available here , as the output would always be JSON - formatted .
 = = = = = API DOCS = = = = =
 Update all related inventory sources of the given inventory .
 : param pk : Primary key of the given inventory .
 : type pk : int
 : param ` * * kwargs ` : Keyword arguments list of available fields used for searching resource objects .
 : returns : A JSON object of update status of the given inventory .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation:
res = self . get ( pk = pk , ** kwargs )
url = self . endpoint + '%d/%s/' % ( res [ 'id' ] , 'update_inventory_sources' )
return client . post ( url , data = { } ) . json ( )


signature:
def read ( self , * args , ** kwargs ) :
    '''Do extra processing so we can display the actor field as
 a top - level field'''

implementation:
if 'actor' in kwargs :
    kwargs [ 'actor' ] = kwargs . pop ( 'actor' )
r = super ( Resource , self ) . read ( * args , ** kwargs )
if 'results' in r :
    for d in r [ 'results' ] :
        self . _promote_actor ( d )
else :
    self . _promote_actor ( d )
return r


signature:
def log ( s , header = '' , file = sys . stderr , nl = 1 , ** kwargs ) :
    """Log the given output to stderr if and only if we are in
 verbose mode .
 If we are not in verbose mode , this is a no - op ."""

implementation:
# Sanity check : If we are not in verbose mode , this is a no - op .
if not settings . verbose :
    return
# Construct multi - line string to stderr if header is provided .
if header :
    word_arr = s . split ( ' ' )
    multi = [ ]
    word_arr . insert ( 0 , '%s:' % header . upper ( ) )
    i = 0
    while i < len ( word_arr ) :
        to_add = [ '***' ]
        count = 3
        while count <= 79 :
            count += len ( word_arr [ i ] ) + 1
            if count <= 79 :
                to_add . append ( word_arr [ i ] )
                i += 1
                if i == len ( word_arr ) :
                    break
        # Handle corner case of extra - long word longer than 75 characters .
        if len ( to_add ) == 1 :
            to_add . append ( word_arr [ i ] )
            i += 1
        if i != len ( word_arr ) :
            count -= len ( word_arr [ i ] ) + 1
        to_add . append ( '*' * ( 78 - count ) )
        multi . append ( ' ' . join ( to_add ) )
    s = '\n' . join ( multi )
    lines = len ( multi )
else :
    lines = 1
# If ` nl ` is an int greater than the number of rows of a message ,
# add the appropriate newlines to the output .
if isinstance ( nl , int ) and nl > lines :
    s += '\n' * ( nl - lines )
# Output to stderr .
return secho ( s , file = file , ** kwargs )


signature:
def configure_model ( self , attrs , field_name ) :
    '''Hook for ResourceMeta class to call when initializing model class .
 Saves fields obtained from resource class backlinks'''

implementation:
self . relationship = field_name
self . _set_method_names ( relationship = field_name )
if self . res_name is None :
    self . res_name = grammar . singularize ( attrs . get ( 'endpoint' , 'unknown' ) . strip ( '/' ) )


signature:
def _produce_raw_method ( self ) :
    '''Returns a callable which becomes the associate or disassociate
 method for the related field .
 Method can be overridden to add additional functionality , but
 ` _ produce _ method ` may also need to be subclassed to decorate
 it appropriately .'''

implementation:
def method ( res_self , ** kwargs ) :
    obj_pk = kwargs . get ( method . _res_name )
    other_obj_pk = kwargs . get ( method . _other_name )
    internal_method = getattr ( res_self , method . _internal_name )
    return internal_method ( method . _relationship , obj_pk , other_obj_pk )
return method


signature:
def create ( self , fail_on_found = False , force_on_exists = False , ** kwargs ) :
    """Create a new label .
 There are two types of label creation : isolatedly creating a new label and creating a new label under
 a job template . Here the two types are discriminated by whether to provide - - job - template option .
 Fields in the resource ' s ` identity ` tuple are used for a lookup ; if a match is found , then no - op ( unless
 ` force _ on _ exists ` is set ) but do not fail ( unless ` fail _ on _ found ` is set ) .
 = = = = = API DOCS = = = = =
 Create a label .
 : param job _ template : Primary key or name of the job template for the created label to associate to .
 : type job _ template : str
 : param fail _ on _ found : Flag that if set , the operation fails if an object matching the unique criteria
 already exists .
 : type fail _ on _ found : bool
 : param force _ on _ exists : Flag that if set , then if a match is found on unique fields , other fields will
 be updated to the provided values . ; If unset , a match causes the request to be
 a no - op .
 : type force _ on _ exists : bool
 : param ` * * kwargs ` : Keyword arguments which , all together , will be used as POST body to create the
 resource object .
 : returns : A dictionary combining the JSON output of the created resource , as well as two extra fields :
 " changed " , a flag indicating if the resource is created successfully ; " id " , an integer which
 is the primary key of the created object .
 : rtype : dict
 : raises tower _ cli . exceptions . TowerCLIError : When the label already exists and ` ` fail _ on _ found ` ` flag is on .
 = = = = = API DOCS = = = = ="""

implementation:
jt_id = kwargs . pop ( 'job_template' , None )
old_endpoint = self . endpoint
if jt_id is not None :
    jt = get_resource ( 'job_template' )
    jt . get ( pk = jt_id )
    try :
        label_id = self . get ( name = kwargs . get ( 'name' , None ) , organization = kwargs . get ( 'organization' , None ) ) [ 'id' ]
    except exc . NotFound :
        pass
    else :
        if fail_on_found :
            raise exc . TowerCLIError ( 'Label already exists and fail-on-found is switched on. Please use' ' "associate_label" method of job_template instead.' )
        else :
            debug . log ( 'Label already exists, associating with job template.' , header = 'details' )
            return jt . associate_label ( job_template = jt_id , label = label_id )
    self . endpoint = '/job_templates/%d/labels/' % jt_id
result = super ( Resource , self ) . create ( fail_on_found = fail_on_found , force_on_exists = force_on_exists , ** kwargs )
self . endpoint = old_endpoint
return result


signature:
def version ( ) :
    """Display full version information ."""

implementation:
# Print out the current version of Tower CLI .
click . echo ( 'Tower CLI %s' % __version__ )
# Print out the current API version of the current code base .
click . echo ( 'API %s' % CUR_API_VERSION )
# Attempt to connect to the Ansible Tower server .
# If we succeed , print a version ; if not , generate a failure .
try :
    r = client . get ( '/config/' )
except RequestException as ex :
    raise exc . TowerCLIError ( 'Could not connect to Ansible Tower.\n%s' % six . text_type ( ex ) )
config = r . json ( )
license = config . get ( 'license_info' , { } ) . get ( 'license_type' , 'open' )
if license == 'open' :
    server_type = 'AWX'
else :
    server_type = 'Ansible Tower'
click . echo ( '%s %s' % ( server_type , config [ 'version' ] ) )
# Print out Ansible version of server
click . echo ( 'Ansible %s' % config [ 'ansible_version' ] )


signature:
def _echo_setting ( key ) :
    """Echo a setting to the CLI ."""

implementation:
value = getattr ( settings , key )
secho ( '%s: ' % key , fg = 'magenta' , bold = True , nl = False )
secho ( six . text_type ( value ) , bold = True , fg = 'white' if isinstance ( value , six . text_type ) else 'cyan' , )


signature:
def config ( key = None , value = None , scope = 'user' , global_ = False , unset = False ) :
    """Read or write tower - cli configuration .
 ` tower config ` saves the given setting to the appropriate Tower CLI ;
 either the user ' s ~ / . tower _ cli . cfg file , or the / etc / tower / tower _ cli . cfg
 file if - - global is used .
 Writing to / etc / tower / tower _ cli . cfg is likely to require heightened
 permissions ( in other words , sudo ) ."""

implementation:
# If the old - style ` global _ ` option is set , issue a deprecation notice .
if global_ :
    scope = 'global'
    warnings . warn ( 'The `--global` option is deprecated and will be ' 'removed. Use `--scope=global` to get the same effect.' , DeprecationWarning )
# If no key was provided , print out the current configuration
# in play .
if not key :
    seen = set ( )
    parser_desc = { 'runtime' : 'Runtime options.' , 'environment' : 'Options from environment variables.' , 'local' : 'Local options (set with `tower-cli config ' '--scope=local`; stored in .tower_cli.cfg of this ' 'directory or a parent)' , 'user' : 'User options (set with `tower-cli config`; stored in ' '~/.tower_cli.cfg).' , 'global' : 'Global options (set with `tower-cli config ' '--scope=global`, stored in /etc/tower/tower_cli.cfg).' , 'defaults' : 'Defaults.' , }
    # Iterate over each parser ( English : location we can get settings from )
    # and print any settings that we haven ' t already seen .
    # We iterate over settings from highest precedence to lowest , so any
    # seen settings are overridden by the version we iterated over already .
    click . echo ( '' )
    for name , parser in zip ( settings . _parser_names , settings . _parsers ) : # Determine if we ' re going to see any options in this
    # parser that get echoed .
        will_echo = False
        for option in parser . options ( 'general' ) :
            if option in seen :
                continue
            will_echo = True
        # Print a segment header
        if will_echo :
            secho ( '# %s' % parser_desc [ name ] , fg = 'green' , bold = True )
        # Iterate over each option in the parser and , if we haven ' t
        # already seen an option at higher precedence , print it .
        for option in parser . options ( 'general' ) :
            if option in seen :
                continue
            _echo_setting ( option )
            seen . add ( option )
        # Print a nice newline , for formatting .
        if will_echo :
            click . echo ( '' )
    return
# Sanity check : Is this a valid configuration option ? If it ' s not
# a key we recognize , abort .
if not hasattr ( settings , key ) :
    raise exc . TowerCLIError ( 'Invalid configuration option "%s".' % key )
# Sanity check : The combination of a value and - - unset makes no
# sense .
if value and unset :
    raise exc . UsageError ( 'Cannot provide both a value and --unset.' )
# If a key was provided but no value was provided , then just
# print the current value for that key .
if key and not value and not unset :
    _echo_setting ( key )
    return
# Okay , so we ' re * writing * a key . Let ' s do this .
# First , we need the appropriate file .
filename = os . path . expanduser ( '~/.tower_cli.cfg' )
if scope == 'global' :
    if not os . path . isdir ( '/etc/tower/' ) :
        raise exc . TowerCLIError ( '/etc/tower/ does not exist, and this ' 'command cowardly declines to create it.' )
    filename = '/etc/tower/tower_cli.cfg'
elif scope == 'local' :
    filename = '.tower_cli.cfg'
# Read in the appropriate config file , write this value , and save
# the result back to the file .
parser = Parser ( )
parser . add_section ( 'general' )
parser . read ( filename )
if unset :
    parser . remove_option ( 'general' , key )
else :
    parser . set ( 'general' , key , value )
with open ( filename , 'w' ) as config_file :
    parser . write ( config_file )
# Give rw permissions to user only fix for issue number 48
try :
    os . chmod ( filename , stat . S_IRUSR | stat . S_IWUSR )
except Exception as e :
    warnings . warn ( 'Unable to set permissions on {0} - {1} ' . format ( filename , e ) , UserWarning )
click . echo ( 'Configuration updated successfully.' )


signature:
def login ( username , password , scope , client_id , client_secret , verbose ) :
    """Retrieves and stores an OAuth2 personal auth token ."""

implementation:
if not supports_oauth ( ) :
    raise exc . TowerCLIError ( 'This version of Tower does not support OAuth2.0. Set credentials using tower-cli config.' )
# Explicitly set a basic auth header for PAT acquisition ( so that we don ' t
# try to auth w / an existing user + pass or oauth2 token in a config file )
req = collections . namedtuple ( 'req' , 'headers' ) ( { } )
if client_id and client_secret :
    HTTPBasicAuth ( client_id , client_secret ) ( req )
    req . headers [ 'Content-Type' ] = 'application/x-www-form-urlencoded'
    r = client . post ( '/o/token/' , data = { "grant_type" : "password" , "username" : username , "password" : password , "scope" : scope } , headers = req . headers )
elif client_id :
    req . headers [ 'Content-Type' ] = 'application/x-www-form-urlencoded'
    r = client . post ( '/o/token/' , data = { "grant_type" : "password" , "username" : username , "password" : password , "client_id" : client_id , "scope" : scope } , headers = req . headers )
else :
    HTTPBasicAuth ( username , password ) ( req )
    r = client . post ( '/users/{}/personal_tokens/' . format ( username ) , data = { "description" : "Tower CLI" , "application" : None , "scope" : scope } , headers = req . headers )
if r . ok :
    result = r . json ( )
    result . pop ( 'summary_fields' , None )
    result . pop ( 'related' , None )
    if client_id :
        token = result . pop ( 'access_token' , None )
    else :
        token = result . pop ( 'token' , None )
    if settings . verbose : # only print the actual token if - v
        result [ 'token' ] = token
    secho ( json . dumps ( result , indent = 1 ) , fg = 'blue' , bold = True )
    config . main ( [ 'oauth_token' , token , '--scope=user' ] )


signature:
def receive ( organization = None , user = None , team = None , credential_type = None , credential = None , notification_template = None , inventory_script = None , inventory = None , project = None , job_template = None , workflow = None , all = None ) :
    """Export assets from Tower .
 ' tower receive ' exports one or more assets from a Tower instance
 For all of the possible assets types the TEXT can either be the assets name
 ( or username for the case of a user ) or the keyword all . Specifying all
 will export all of the assets of that type ."""

implementation:
from tower_cli . cli . transfer . receive import Receiver
receiver = Receiver ( )
assets_to_export = { }
for asset_type in SEND_ORDER :
    assets_to_export [ asset_type ] = locals ( ) [ asset_type ]
receiver . receive ( all = all , asset_input = assets_to_export )


signature:
def send ( source = None , prevent = None , exclude = None , secret_management = 'default' , no_color = False ) :
    """Import assets into Tower .
 ' tower send ' imports one or more assets into a Tower instance
 The import can take either JSON or YAML .
 Data can be sent on stdin ( i . e . from tower - cli receive pipe ) and / or from files
 or directories passed as parameters .
 If a directory is specified only files that end in . json , . yaml or . yml will be
 imported . Other files will be ignored ."""

implementation:
from tower_cli . cli . transfer . send import Sender
sender = Sender ( no_color )
sender . send ( source , prevent , exclude , secret_management )


signature:
def empty ( organization = None , user = None , team = None , credential_type = None , credential = None , notification_template = None , inventory_script = None , inventory = None , project = None , job_template = None , workflow = None , all = None , no_color = False ) :
    """Empties assets from Tower .
 ' tower empty ' removes all assets from Tower"""

implementation:
# Create an import / export object
from tower_cli . cli . transfer . cleaner import Cleaner
destroyer = Cleaner ( no_color )
assets_to_export = { }
for asset_type in SEND_ORDER :
    assets_to_export [ asset_type ] = locals ( ) [ asset_type ]
destroyer . go_ham ( all = all , asset_input = assets_to_export )


signature:
def parse_requirements ( filename ) :
    """Parse out a list of requirements from the given requirements
 requirements file ."""

implementation:
reqs = [ ]
version_spec_in_play = None
# Iterate over each line in the requirements file .
for line in open ( filename , 'r' ) . read ( ) . strip ( ) . split ( '\n' ) : # Sanity check : Is this an empty line ?
# If so , do nothing .
    if not line . strip ( ) :
        continue
    # If this is just a plain requirement ( not a comment ) , then
    # add it to the requirements list .
    if not line . startswith ( '#' ) :
        reqs . append ( line )
        continue
    # " Header " comments take the form of " = = = Python { op } { version } = = = " ,
    # and make the requirement only matter for those versions .
    # If this line is a header comment , parse it .
    match = re . search ( r'^# === [Pp]ython (?P<op>[<>=]{1,2}) ' r'(?P<major>[\d])\.(?P<minor>[\d]+) ===[\s]*$' , line )
    if match :
        version_spec_in_play = match . groupdict ( )
        for key in ( 'major' , 'minor' ) :
            version_spec_in_play [ key ] = int ( version_spec_in_play [ key ] )
        continue
    # If this is a comment that otherwise looks like a package , then it
    # should be a package applying only to the current version spec .
    # We can identify something that looks like a package by a lack
    # of any spaces .
    if ' ' not in line [ 1 : ] . strip ( ) and version_spec_in_play :
        package = line [ 1 : ] . strip ( )
        # Sanity check : Is our version of Python one of the ones currently
        # in play ?
        op = version_spec_in_play [ 'op' ]
        vspec = ( version_spec_in_play [ 'major' ] , version_spec_in_play [ 'minor' ] )
        if '=' in op and sys . version_info [ 0 : 2 ] == vspec :
            reqs . append ( package )
        elif '>' in op and sys . version_info [ 0 : 2 ] > vspec :
            reqs . append ( package )
        elif '<' in op and sys . version_info [ 0 : 2 ] < vspec :
            reqs . append ( package )
# Okay , we should have an entire list of requirements now .
return reqs


signature:
def combine_files ( * args ) :
    """returns a string of all the strings in * args combined together ,
 with two line breaks between them"""

implementation:
file_contents = [ ]
for filename in args :
    with codecs . open ( filename , mode = 'r' , encoding = 'utf8' ) as f :
        file_contents . append ( f . read ( ) )
return "\n\n" . join ( file_contents )


signature:
def create ( self , organization = None , monitor = False , wait = False , timeout = None , fail_on_found = False , force_on_exists = False , ** kwargs ) :
    """Create a new item of resource , with or w / o org .
 This would be a shared class with user , but it needs the ability
 to monitor if the flag is set .
 = = = = = API DOCS = = = = =
 Create a project and , if related flags are set , monitor or wait the triggered initial project update .
 : param monitor : Flag that if set , immediately calls ` ` monitor ` ` on the newly triggered project update
 rather than exiting with a success .
 : type monitor : bool
 : param wait : Flag that if set , monitor the status of the triggered project update , but do not print
 while it is in progress .
 : type wait : bool
 : param timeout : If provided with ` ` monitor ` ` flag set , this attempt will time out after the given number
 of seconds .
 : type timeout : bool
 : param fail _ on _ found : Flag that if set , the operation fails if an object matching the unique criteria
 already exists .
 : type fail _ on _ found : bool
 : param force _ on _ exists : Flag that if set , then if a match is found on unique fields , other fields will
 be updated to the provided values . ; If unset , a match causes the request to be
 a no - op .
 : type force _ on _ exists : bool
 : param ` * * kwargs ` : Keyword arguments which , all together , will be used as POST body to create the
 resource object .
 : returns : A dictionary combining the JSON output of the created resource , as well as two extra fields :
 " changed " , a flag indicating if the resource is created successfully ; " id " , an integer which
 is the primary key of the created object .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation:
if 'job_timeout' in kwargs and 'timeout' not in kwargs :
    kwargs [ 'timeout' ] = kwargs . pop ( 'job_timeout' )
post_associate = False
if organization : # Processing the organization flag depends on version
    debug . log ( 'Checking Organization Relationship.' , header = 'details' )
    r = client . options ( '/projects/' )
    if 'organization' in r . json ( ) . get ( 'actions' , { } ) . get ( 'POST' , { } ) :
        kwargs [ 'organization' ] = organization
    else :
        post_associate = True
# First , run the create method , ignoring the organization given
answer = super ( Resource , self ) . write ( create_on_missing = True , fail_on_found = fail_on_found , force_on_exists = force_on_exists , ** kwargs )
project_id = answer [ 'id' ]
# If an organization is given , associate it here
if post_associate : # Get the organization from Tower , will lookup name if needed
    org_resource = get_resource ( 'organization' )
    org_data = org_resource . get ( organization )
    org_pk = org_data [ 'id' ]
    debug . log ( "associating the project with its organization" , header = 'details' , nl = 1 )
    org_resource . _assoc ( 'projects' , org_pk , project_id )
# if the monitor flag is set , wait for the SCM to update
if monitor and answer . get ( 'changed' , False ) :
    return self . monitor ( pk = None , parent_pk = project_id , timeout = timeout )
elif wait and answer . get ( 'changed' , False ) :
    return self . wait ( pk = None , parent_pk = project_id , timeout = timeout )
return answer


signature:
def modify ( self , pk = None , create_on_missing = False , ** kwargs ) :
    """Modify an already existing .
 To edit the project ' s organizations , see help for organizations .
 Fields in the resource ' s ` identity ` tuple can be used in lieu of a
 primary key for a lookup ; in such a case , only other fields are
 written .
 To modify unique fields , you must use the primary key for the lookup .
 = = = = = API DOCS = = = = =
 Modify an already existing project .
 : param pk : Primary key of the resource to be modified .
 : type pk : int
 : param create _ on _ missing : Flag that if set , a new object is created if ` ` pk ` ` is not set and objects
 matching the appropriate unique criteria is not found .
 : type create _ on _ missing : bool
 : param ` * * kwargs ` : Keyword arguments which , all together , will be used as PATCH body to modify the
 resource object . if ` ` pk ` ` is not set , key - value pairs of ` ` * * kwargs ` ` which are
 also in resource ' s identity will be used to lookup existing reosource .
 : returns : A dictionary combining the JSON output of the modified resource , as well as two extra fields :
 " changed " , a flag indicating if the resource is successfully updated ; " id " , an integer which
 is the primary key of the updated object .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation:
# Associated with issue # 52 , the organization can ' t be modified
# with the ' modify ' command . This would create confusion about
# whether its flag is an identifier versus a field to modify .
if 'job_timeout' in kwargs and 'timeout' not in kwargs :
    kwargs [ 'timeout' ] = kwargs . pop ( 'job_timeout' )
return super ( Resource , self ) . write ( pk , create_on_missing = create_on_missing , force_on_exists = True , ** kwargs )


signature:
def update ( self , pk = None , create_on_missing = False , monitor = False , wait = False , timeout = None , name = None , organization = None ) :
    """Trigger a project update job within Ansible Tower .
 Only meaningful on non - manual projects .
 = = = = = API DOCS = = = = =
 Update the given project .
 : param pk : Primary key of the project to be updated .
 : type pk : int
 : param monitor : Flag that if set , immediately calls ` ` monitor ` ` on the newly launched project update
 rather than exiting with a success .
 : type monitor : bool
 : param wait : Flag that if set , monitor the status of the project update , but do not print while it is
 in progress .
 : type wait : bool
 : param timeout : If provided with ` ` monitor ` ` flag set , this attempt will time out after the given number
 of seconds .
 : type timeout : int
 : param name : Name of the project to be updated if ` ` pk ` ` is not set .
 : type name : str
 : param organization : Primary key or name of the organization the project to be updated belonging to if
 ` ` pk ` ` is not set .
 : type organization : str
 : returns : Result of subsequent ` ` monitor ` ` call if ` ` monitor ` ` flag is on ; Result of subsequent ` ` wait ` `
 call if ` ` wait ` ` flag is on ; dictionary of " status " if none of the two flags are on .
 : rtype : dict
 : raises tower _ cli . exceptions . CannotStartJob : When the project cannot be updated .
 = = = = = API DOCS = = = = ="""

implementation:
# First , get the appropriate project .
# This should be uniquely identified at this point , and if not , then
# we just want the error that ` get ` will throw to bubble up .
project = self . get ( pk , name = name , organization = organization )
pk = project [ 'id' ]
# Determine whether this project is able to be updated .
debug . log ( 'Asking whether the project can be updated.' , header = 'details' )
result = client . get ( '/projects/%d/update/' % pk )
if not result . json ( ) [ 'can_update' ] :
    raise exc . CannotStartJob ( 'Cannot update project.' )
# Okay , this project can be updated , according to Tower .
# Commence the update .
debug . log ( 'Updating the project.' , header = 'details' )
result = client . post ( '/projects/%d/update/' % pk )
project_update_id = result . json ( ) [ 'project_update' ]
# If we were told to monitor the project update ' s status , do so .
if monitor :
    return self . monitor ( project_update_id , parent_pk = pk , timeout = timeout )
elif wait :
    return self . wait ( project_update_id , parent_pk = pk , timeout = timeout )
# Return the project update ID .
return { 'id' : project_update_id , 'changed' : True , }


signature:
def status ( self , pk = None , detail = False , ** kwargs ) :
    """Print the status of the most recent update .
 = = = = = API DOCS = = = = =
 Print the status of the most recent update .
 : param pk : Primary key of the resource to retrieve status from .
 : type pk : int
 : param detail : Flag that if set , return the full JSON of the job resource rather than a status summary .
 : type detail : bool
 : param ` * * kwargs ` : Keyword arguments used to look up resource object to retrieve status from if ` ` pk ` `
 is not provided .
 : returns : full loaded JSON of the specified unified job if ` ` detail ` ` flag is on ; trimed JSON containing
 only " elapsed " , " failed " and " status " fields of the unified job if ` ` detail ` ` flag is off .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation:
# Obtain the most recent project update
job = self . last_job_data ( pk , ** kwargs )
# In most cases , we probably only want to know the status of the job
# and the amount of time elapsed . However , if we were asked for
# verbose information , provide it .
if detail :
    return job
# Print just the information we need .
return { 'elapsed' : job [ 'elapsed' ] , 'failed' : job [ 'failed' ] , 'status' : job [ 'status' ] , }


signature:
def convert ( self , value , param , ctx ) :
    """Return file content if file , else , return value as - is"""

implementation:
# Protect against corner cases of invalid inputs
if not isinstance ( value , str ) :
    return value
if isinstance ( value , six . binary_type ) :
    value = value . decode ( 'UTF-8' )
# Read from a file under these cases
if value . startswith ( '@' ) :
    filename = os . path . expanduser ( value [ 1 : ] )
    file_obj = super ( Variables , self ) . convert ( filename , param , ctx )
    if hasattr ( file_obj , 'read' ) : # Sometimes click . File may return a buffer and not a string
        return file_obj . read ( )
    return file_obj
# No file , use given string
return value


signature:
def convert ( self , value , param , ctx ) :
    """Match against the appropriate choice value using the superclass
 implementation , and then return the actual choice ."""

implementation:
choice = super ( MappedChoice , self ) . convert ( value , param , ctx )
ix = self . choices . index ( choice )
return self . actual_choices [ ix ]


signature:
def convert ( self , value , param , ctx ) :
    """Return the appropriate integer value . If a non - integer is
 provided , attempt a name - based lookup and return the primary key ."""

implementation:
resource = tower_cli . get_resource ( self . resource_name )
# Ensure that None is passed through without trying to
# do anything .
if value is None :
    return None
# If we were already given an integer , do nothing .
# This ensures that the convert method is idempotent .
if isinstance ( value , int ) :
    return value
# Do we have a string that contains only digits ?
# If so , then convert it to an integer and return it .
if re . match ( r'^[\d]+$' , value ) :
    return int ( value )
# Special case to allow disassociations
if value == 'null' :
    return value
# Okay , we have a string . Try to do a name - based lookup on the
# resource , and return back the ID that we get from that .
# This has the chance of erroring out , which is fine .
try :
    debug . log ( 'The %s field is given as a name; ' 'looking it up.' % param . name , header = 'details' )
    lookup_data = { resource . identity [ - 1 ] : value }
    rel = resource . get ( ** lookup_data )
except exc . MultipleResults :
    raise exc . MultipleRelatedError ( 'Cannot look up {0} exclusively by name, because multiple {0} ' 'objects exist with that name.\n' 'Please send an ID. You can get the ID for the {0} you want ' 'with:\n' '  tower-cli {0} list --name "{1}"' . format ( self . resource_name , value ) , )
except exc . TowerCLIError as ex :
    raise exc . RelatedError ( 'Could not get %s. %s' % ( self . resource_name , str ( ex ) ) )
# Done ! Return the ID .
return rel [ 'id' ]


signature:
def _parent_filter ( self , parent , relationship , ** kwargs ) :
    """Returns filtering parameters to limit a search to the children
 of a particular node by a particular relationship ."""

implementation:
if parent is None or relationship is None :
    return { }
parent_filter_kwargs = { }
query_params = ( ( self . _reverse_rel_name ( relationship ) , parent ) , )
parent_filter_kwargs [ 'query' ] = query_params
if kwargs . get ( 'workflow_job_template' , None ) is None :
    parent_data = self . read ( pk = parent ) [ 'results' ] [ 0 ]
    parent_filter_kwargs [ 'workflow_job_template' ] = parent_data [ 'workflow_job_template' ]
return parent_filter_kwargs


signature:
def associate_success_node ( self , parent , child = None , ** kwargs ) :
    """Add a node to run on success .
 = = = = = API DOCS = = = = =
 Add a node to run on success .
 : param parent : Primary key of parent node to associate success node to .
 : type parent : int
 : param child : Primary key of child node to be associated .
 : type child : int
 : param ` * * kwargs ` : Fields used to create child node if ` ` child ` ` is not provided .
 : returns : Dictionary of only one key " changed " , which indicates whether the association succeeded .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation: return self . _assoc_or_create ( 'success' , parent , child , ** kwargs )


signature:
def disassociate_success_node ( self , parent , child ) :
    """Remove success node .
 The resulatant 2 nodes will both become root nodes .
 = = = = = API DOCS = = = = =
 Remove success node .
 : param parent : Primary key of parent node to disassociate success node from .
 : type parent : int
 : param child : Primary key of child node to be disassociated .
 : type child : int
 : returns : Dictionary of only one key " changed " , which indicates whether the disassociation succeeded .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation: return self . _disassoc ( self . _forward_rel_name ( 'success' ) , parent , child )


signature:
def associate_failure_node ( self , parent , child = None , ** kwargs ) :
    """Add a node to run on failure .
 = = = = = API DOCS = = = = =
 Add a node to run on failure .
 : param parent : Primary key of parent node to associate failure node to .
 : type parent : int
 : param child : Primary key of child node to be associated .
 : type child : int
 : param ` * * kwargs ` : Fields used to create child node if ` ` child ` ` is not provided .
 : returns : Dictionary of only one key " changed " , which indicates whether the association succeeded .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation: return self . _assoc_or_create ( 'failure' , parent , child , ** kwargs )


signature:
def disassociate_failure_node ( self , parent , child ) :
    """Remove a failure node link .
 The resulatant 2 nodes will both become root nodes .
 = = = = = API DOCS = = = = =
 Remove a failure node link .
 : param parent : Primary key of parent node to disassociate failure node from .
 : type parent : int
 : param child : Primary key of child node to be disassociated .
 : type child : int
 : returns : Dictionary of only one key " changed " , which indicates whether the disassociation succeeded .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation: return self . _disassoc ( self . _forward_rel_name ( 'failure' ) , parent , child )


signature:
def associate_always_node ( self , parent , child = None , ** kwargs ) :
    """Add a node to always run after the parent is finished .
 = = = = = API DOCS = = = = =
 Add a node to always run after the parent is finished .
 : param parent : Primary key of parent node to associate always node to .
 : type parent : int
 : param child : Primary key of child node to be associated .
 : type child : int
 : param ` * * kwargs ` : Fields used to create child node if ` ` child ` ` is not provided .
 : returns : Dictionary of only one key " changed " , which indicates whether the association succeeded .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation: return self . _assoc_or_create ( 'always' , parent , child , ** kwargs )


signature:
def disassociate_always_node ( self , parent , child ) :
    """Remove an always node link .
 The resultant 2 nodes will both become root nodes .
 = = = = = API DOCS = = = = =
 Remove an always node link .
 : param parent : Primary key of parent node to disassociate always node from .
 : type parent : int
 : param child : Primary key of child node to be disassociated .
 : type child : int
 : returns : Dictionary of only one key " changed " , which indicates whether the disassociation succeeded .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation: return self . _disassoc ( self . _forward_rel_name ( 'always' ) , parent , child )


signature:
def obj_res ( data , fail_on = [ 'type' , 'obj' , 'res' ] ) :
    """Given some CLI input data ,
 Returns the following and their types :
 obj - the role grantee
 res - the resource that the role applies to"""

implementation:
errors = [ ]
if not data . get ( 'type' , None ) and 'type' in fail_on :
    errors += [ 'You must provide a role type to use this command.' ]
# Find the grantee , and remove them from resource _ list
obj = None
obj_type = None
for fd in ACTOR_FIELDS :
    if data . get ( fd , False ) :
        if not obj :
            obj = data [ fd ]
            obj_type = fd
        else :
            errors += [ 'You can not give a role to a user ' 'and team at the same time.' ]
            break
if not obj and 'obj' in fail_on :
    errors += [ 'You must specify either user or ' 'team to use this command.' ]
# Out of the resource list , pick out available valid resource field
res = None
res_type = None
for fd in RESOURCE_FIELDS :
    if data . get ( fd , False ) :
        if not res :
            res = data [ fd ]
            res_type = fd
            if res_type == 'target_team' :
                res_type = 'team'
        else :
            errors += [ 'You can only give a role to one ' 'type of resource at a time.' ]
            break
if not res and 'res' in fail_on :
    errors += [ 'You must specify a target resource ' 'to use this command.' ]
if errors :
    raise exc . UsageError ( "\n" . join ( errors ) )
return obj , obj_type , res , res_type


signature:
def data_endpoint ( cls , in_data , ignore = [ ] ) :
    """Converts a set of CLI input arguments , ` in _ data ` , into
 request data and an endpoint that can be used to look
 up a role or list of roles .
 Also changes the format of ` type ` in data to what the server
 expects for the role model , as it exists in the database ."""

implementation:
obj , obj_type , res , res_type = cls . obj_res ( in_data , fail_on = [ ] )
data = { }
if 'obj' in ignore :
    obj = None
if 'res' in ignore :
    res = None
# Input fields are not actually present on role model , and all have
# to be managed as individual special - cases
if obj and obj_type == 'user' :
    data [ 'members__in' ] = obj
if obj and obj_type == 'team' :
    endpoint = '%s/%s/roles/' % ( grammar . pluralize ( obj_type ) , obj )
    if res is not None : # For teams , this is the best lookup we can do
    # without making the additional request for its member _ role
        data [ 'object_id' ] = res
elif res :
    endpoint = '%s/%s/object_roles/' % ( grammar . pluralize ( res_type ) , res )
else :
    endpoint = '/roles/'
if in_data . get ( 'type' , False ) :
    data [ 'role_field' ] = '%s_role' % in_data [ 'type' ] . lower ( )
# Add back fields unrelated to role lookup , such as all _ pages
for key , value in in_data . items ( ) :
    if key not in RESOURCE_FIELDS and key not in [ 'type' , 'user' , 'team' ] :
        data [ key ] = value
return data , endpoint


signature:
def populate_resource_columns ( item_dict ) :
    """Operates on item _ dict
 Promotes the resource _ name and resource _ type fields to the
 top - level of the serialization so they can be printed as columns .
 Also makes a copies name field to type , which is a default column ."""

implementation:
item_dict [ 'type' ] = item_dict [ 'name' ]
if len ( item_dict [ 'summary_fields' ] ) == 0 : # Singleton roles omit these fields
    item_dict [ 'resource_name' ] = None
    item_dict [ 'resource_type' ] = None
else :
    sf = item_dict [ 'summary_fields' ]
    # Explination of fallback state :
    # The situation where resource _ name or resource _ type is not present
    # should not be seen for singleton roles , and where it is seen ,
    # there may be a problem with data quality on the server
    item_dict [ 'resource_name' ] = sf . get ( 'resource_name' , '[unknown]' )
    item_dict [ 'resource_type' ] = sf . get ( 'resource_type' , '[unknown]' )


signature:
def set_display_columns ( self , set_true = [ ] , set_false = [ ] ) :
    """Add or remove columns from the output ."""

implementation:
for i in range ( len ( self . fields ) ) :
    if self . fields [ i ] . name in set_true :
        self . fields [ i ] . display = True
    elif self . fields [ i ] . name in set_false :
        self . fields [ i ] . display = False


signature:
def configure_display ( self , data , kwargs = None , write = False ) :
    """Populates columns and sets display attribute as needed .
 Operates on data ."""

implementation:
if settings . format != 'human' :
    return
    # This is only used for human format
if write :
    obj , obj_type , res , res_type = self . obj_res ( kwargs )
    data [ 'type' ] = kwargs [ 'type' ]
    data [ obj_type ] = obj
    data [ res_type ] = res
    self . set_display_columns ( set_false = [ 'team' if obj_type == 'user' else 'user' ] , set_true = [ 'target_team' if res_type == 'team' else res_type ] )
else :
    self . set_display_columns ( set_false = [ 'user' , 'team' ] , set_true = [ 'resource_name' , 'resource_type' ] )
    if 'results' in data :
        for i in range ( len ( data [ 'results' ] ) ) :
            self . populate_resource_columns ( data [ 'results' ] [ i ] )
    else :
        self . populate_resource_columns ( data )


signature:
def role_write ( self , fail_on_found = False , disassociate = False , ** kwargs ) :
    """Re - implementation of the parent ` write ` method specific to roles .
 Adds a grantee ( user or team ) to the resource ' s role ."""

implementation:
# Get the role , using only the resource data
data , self . endpoint = self . data_endpoint ( kwargs , ignore = [ 'obj' ] )
debug . log ( 'Checking if role exists.' , header = 'details' )
response = self . read ( pk = None , fail_on_no_results = True , fail_on_multiple_results = True , ** data )
role_data = response [ 'results' ] [ 0 ]
role_id = role_data [ 'id' ]
# Role exists , change display settings to output something
self . configure_display ( role_data , kwargs , write = True )
# Check if user / team has this role
# Implictly , force _ on _ exists is false for roles
obj , obj_type , res , res_type = self . obj_res ( kwargs )
debug . log ( 'Checking if %s already has role.' % obj_type , header = 'details' )
data , self . endpoint = self . data_endpoint ( kwargs )
data [ 'content_type__model' ] = res_type . replace ( '_' , '' )
response = self . read ( pk = None , fail_on_no_results = False , fail_on_multiple_results = False , ** data )
msg = ''
if response [ 'count' ] > 0 and not disassociate :
    msg = 'This %s is already a member of the role.' % obj_type
elif response [ 'count' ] == 0 and disassociate :
    msg = 'This %s is already a non-member of the role.' % obj_type
if msg :
    role_data [ 'changed' ] = False
    if fail_on_found :
        raise exc . NotFound ( msg )
    else :
        debug . log ( msg , header = 'DECISION' )
        return role_data
# Add or remove the user / team to the role
debug . log ( 'Attempting to %s the %s in this role.' % ( 'remove' if disassociate else 'add' , obj_type ) , header = 'details' )
post_data = { 'id' : role_id }
if disassociate :
    post_data [ 'disassociate' ] = True
client . post ( '%s/%s/roles/' % ( grammar . pluralize ( obj_type ) , obj ) , data = post_data )
role_data [ 'changed' ] = True
return role_data


signature:
def list ( self , ** kwargs ) :
    """Return a list of roles .
 = = = = = API DOCS = = = = =
 Retrieve a list of objects .
 : param all _ pages : Flag that if set , collect all pages of content from the API when returning results .
 : type all _ pages : bool
 : param page : The page to show . Ignored if all _ pages is set .
 : type page : int
 : param query : Contains 2 - tuples used as query parameters to filter resulting resource objects .
 : type query : list
 : param ` * * kwargs ` : Keyword arguments list of available fields used for searching resource objects .
 : returns : A JSON object containing details of all resource objects returned by Tower backend .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation:
data , self . endpoint = self . data_endpoint ( kwargs )
r = super ( Resource , self ) . list ( ** data )
# Change display settings and data format for human consumption
self . configure_display ( r )
return r


signature:
def get ( self , pk = None , ** kwargs ) :
    """Get information about a role .
 = = = = = API DOCS = = = = =
 Retrieve one and exactly one object .
 : param pk : Primary key of the resource to be read . Tower CLI will only attempt to read * that * object
 if ` ` pk ` ` is provided ( not ` ` None ` ` ) .
 : type pk : int
 : param ` * * kwargs ` : Keyword arguments used to look up resource object to retrieve if ` ` pk ` ` is not provided .
 : returns : loaded JSON of the retrieved resource object .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation:
if kwargs . pop ( 'include_debug_header' , True ) :
    debug . log ( 'Getting the role record.' , header = 'details' )
data , self . endpoint = self . data_endpoint ( kwargs )
response = self . read ( pk = pk , fail_on_no_results = True , fail_on_multiple_results = True , ** data )
item_dict = response [ 'results' ] [ 0 ]
self . configure_display ( item_dict )
return item_dict


signature:
def grant ( self , fail_on_found = False , ** kwargs ) :
    """Add a user or a team to a role . Required information :
 1 ) Type of the role
 2 ) Resource of the role , inventory , credential , or any other
 3 ) A user or a team to add to the role
 = = = = = API DOCS = = = = =
 Add a user or a team to a role . Required information :
 * Type of the role .
 * Resource of the role , inventory , credential , or any other .
 * A user or a team to add to the role .
 : param fail _ on _ found : Flag that if set , the operation fails if a user / team already has the role .
 : type fail _ on _ found : bool
 : param ` * * kwargs ` : The user to be associated and the role to associate .
 : returns : parsed JSON of role grant .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation: return self . role_write ( fail_on_found = fail_on_found , ** kwargs )


signature:
def revoke ( self , fail_on_found = False , ** kwargs ) :
    """Remove a user or a team from a role . Required information :
 1 ) Type of the role
 2 ) Resource of the role , inventory , credential , or any other
 3 ) A user or a team to add to the role
 = = = = = API DOCS = = = = =
 Remove a user or a team from a role . Required information :
 * Type of the role .
 * Resource of the role , inventory , credential , or any other .
 * A user or a team to add to the role .
 : param fail _ on _ found : Flag that if set , the operation fails if a user / team dose not have the role .
 : type fail _ on _ found : bool
 : param ` * * kwargs ` : The user to be disassociated and the role to disassociate .
 : returns : parsed JSON of role revoke .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation: return self . role_write ( fail_on_found = fail_on_found , disassociate = True , ** kwargs )


signature:
def _compare_node_lists ( old , new ) :
    '''Investigate two lists of workflow TreeNodes and categorize them .
 There will be three types of nodes after categorization :
 1 . Nodes that only exists in the new list . These nodes will later be
 created recursively .
 2 . Nodes that only exists in the old list . These nodes will later be
 deleted recursively .
 3 . Node pairs that makes an exact match . These nodes will be further
 investigated .
 Corresponding nodes of old and new lists will be distinguished by their
 unified _ job _ template value . A special case is that both the old and the new
 lists contain one type of node , say A , and at least one of them contains
 duplicates . In this case all A nodes in the old list will be categorized as
 to - be - deleted and all A nodes in the new list will be categorized as
 to - be - created .'''

implementation:
to_expand = [ ]
to_delete = [ ]
to_recurse = [ ]
old_records = { }
new_records = { }
for tree_node in old :
    old_records . setdefault ( tree_node . unified_job_template , [ ] )
    old_records [ tree_node . unified_job_template ] . append ( tree_node )
for tree_node in new :
    new_records . setdefault ( tree_node . unified_job_template , [ ] )
    new_records [ tree_node . unified_job_template ] . append ( tree_node )
for ujt_id in old_records :
    if ujt_id not in new_records :
        to_delete . extend ( old_records [ ujt_id ] )
        continue
    old_list = old_records [ ujt_id ]
    new_list = new_records . pop ( ujt_id )
    if len ( old_list ) == 1 and len ( new_list ) == 1 :
        to_recurse . append ( ( old_list [ 0 ] , new_list [ 0 ] ) )
    else :
        to_delete . extend ( old_list )
        to_expand . extend ( new_list )
for nodes in new_records . values ( ) :
    to_expand . extend ( nodes )
return to_expand , to_delete , to_recurse


signature:
def _workflow_node_structure ( node_results ) :
    '''Takes the list results from the API in ` node _ results ` and
 translates this data into a dictionary organized in a
 human - readable heirarchial structure'''

implementation:
# Build list address translation , and create backlink lists
node_list_pos = { }
for i , node_result in enumerate ( node_results ) :
    for rel in [ 'success' , 'failure' , 'always' ] :
        node_result [ '{0}_backlinks' . format ( rel ) ] = [ ]
    node_list_pos [ node_result [ 'id' ] ] = i
# Populate backlink lists
for node_result in node_results :
    for rel in [ 'success' , 'failure' , 'always' ] :
        for sub_node_id in node_result [ '{0}_nodes' . format ( rel ) ] :
            j = node_list_pos [ sub_node_id ]
            node_results [ j ] [ '{0}_backlinks' . format ( rel ) ] . append ( node_result [ 'id' ] )
# Find the root nodes
root_nodes = [ ]
for node_result in node_results :
    is_root = True
    for rel in [ 'success' , 'failure' , 'always' ] :
        if node_result [ '{0}_backlinks' . format ( rel ) ] != [ ] :
            is_root = False
            break
    if is_root :
        root_nodes . append ( node_result [ 'id' ] )
# Create network dictionary recursively from root nodes
def branch_schema ( node_id ) :
    i = node_list_pos [ node_id ]
    node_dict = node_results [ i ]
    ret_dict = { "id" : node_id }
    for fd in NODE_STANDARD_FIELDS :
        val = node_dict . get ( fd , None )
        if val is not None :
            if fd == 'unified_job_template' :
                job_type = node_dict [ 'summary_fields' ] [ 'unified_job_template' ] [ 'unified_job_type' ]
                ujt_key = JOB_TYPES [ job_type ]
                ret_dict [ ujt_key ] = val
            else :
                ret_dict [ fd ] = val
    for rel in [ 'success' , 'failure' , 'always' ] :
        sub_node_id_list = node_dict [ '{0}_nodes' . format ( rel ) ]
        if len ( sub_node_id_list ) == 0 :
            continue
        relationship_name = '{0}_nodes' . format ( rel )
        ret_dict [ relationship_name ] = [ ]
        for sub_node_id in sub_node_id_list :
            ret_dict [ relationship_name ] . append ( branch_schema ( sub_node_id ) )
    return ret_dict
schema_dict = [ ]
for root_node_id in root_nodes :
    schema_dict . append ( branch_schema ( root_node_id ) )
return schema_dict


signature:
def _get_schema ( self , wfjt_id ) :
    """Returns a dictionary that represents the node network of the
 workflow job template"""

implementation:
node_res = get_resource ( 'node' )
node_results = node_res . list ( workflow_job_template = wfjt_id , all_pages = True ) [ 'results' ]
return self . _workflow_node_structure ( node_results )


signature:
def schema ( self , wfjt , node_network = None ) :
    """Convert YAML / JSON content into workflow node objects if
 node _ network param is given .
 If not , print a YAML representation of the node network .
 = = = = = API DOCS = = = = =
 Convert YAML / JSON content into workflow node objects if ` ` node _ network ` ` param is given . If not ,
 print a YAML representation of the node network .
 : param wfjt : Primary key or name of the workflow job template to run schema against .
 : type wfjt : str
 : param node _ network : JSON - or YAML - formatted string representing the topology of the workflow job
 template be updated to .
 : type node _ network : str
 : returns : The latest topology ( possibly after modification ) of the workflow job template .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation:
existing_network = self . _get_schema ( wfjt )
if not isinstance ( existing_network , list ) :
    existing_network = [ ]
if node_network is None :
    if settings . format == 'human' :
        settings . format = 'yaml'
    return existing_network
if hasattr ( node_network , 'read' ) :
    node_network = node_network . read ( )
node_network = string_to_dict ( node_network , allow_kv = False , require_dict = False )
if not isinstance ( node_network , list ) :
    node_network = [ ]
_update_workflow ( [ TreeNode ( x , wfjt , include_id = True ) for x in existing_network ] , [ TreeNode ( x , wfjt ) for x in node_network ] )
if settings . format == 'human' :
    settings . format = 'yaml'
return self . _get_schema ( wfjt )


signature:
def associate_notification_template ( self , workflow , notification_template , status ) :
    """Associate a notification template from this workflow .
 = = = = = API DOCS = = = = =
 Associate a notification template from this workflow job template .
 : param workflow : The workflow job template to associate to .
 : type workflow : str
 : param notification _ template : The notification template to be associated .
 : type notification _ template : str
 : param status : type of notification this notification template should be associated to .
 : type status : str
 : returns : Dictionary of only one key " changed " , which indicates whether the association succeeded .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation: return self . _assoc ( 'notification_templates_%s' % status , workflow , notification_template )


signature:
def disassociate_notification_template ( self , workflow , notification_template , status ) :
    """Disassociate a notification template from this workflow .
 = = = = = API DOCS = = = = =
 Disassociate a notification template from this workflow job template .
 : param job _ template : The workflow job template to disassociate from .
 : type job _ template : str
 : param notification _ template : The notification template to be disassociated .
 : type notification _ template : str
 : param status : type of notification this notification template should be disassociated from .
 : type status : str
 : returns : Dictionary of only one key " changed " , which indicates whether the disassociation succeeded .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation: return self . _disassoc ( 'notification_templates_%s' % status , workflow , notification_template )


signature:
def create ( self , fail_on_found = False , force_on_exists = False , ** kwargs ) :
    """Create a group .
 = = = = = API DOCS = = = = =
 Create a group .
 : param parent : Primary key or name of the group which will be the parent of created group .
 : type parent : str
 : param fail _ on _ found : Flag that if set , the operation fails if an object matching the unique criteria
 already exists .
 : type fail _ on _ found : bool
 : param force _ on _ exists : Flag that if set , then if a match is found on unique fields , other fields will
 be updated to the provided values . ; If unset , a match causes the request to be
 a no - op .
 : type force _ on _ exists : bool
 : param ` * * kwargs ` : Keyword arguments which , all together , will be used as POST body to create the
 resource object .
 : returns : A dictionary combining the JSON output of the created resource , as well as two extra fields :
 " changed " , a flag indicating if the resource is created successfully ; " id " , an integer which
 is the primary key of the created object .
 : rtype : dict
 : raises tower _ cli . exceptions . UsageError : When inventory is not provided in ` ` * * kwargs ` ` and ` ` parent ` `
 is not provided .
 = = = = = API DOCS = = = = ="""

implementation:
if kwargs . get ( 'parent' , None ) :
    parent_data = self . set_child_endpoint ( parent = kwargs [ 'parent' ] , inventory = kwargs . get ( 'inventory' , None ) )
    kwargs [ 'inventory' ] = parent_data [ 'inventory' ]
elif 'inventory' not in kwargs :
    raise exc . UsageError ( 'To create a group, you must provide a parent inventory or parent group.' )
return super ( Resource , self ) . create ( fail_on_found = fail_on_found , force_on_exists = force_on_exists , ** kwargs )


signature:
def list ( self , root = False , ** kwargs ) :
    """Return a list of groups .
 = = = = = API DOCS = = = = =
 Retrieve a list of groups .
 : param root : Flag that if set , only root groups of a specific inventory will be listed .
 : type root : bool
 : param parent : Primary key or name of the group whose child groups will be listed .
 : type parent : str
 : param all _ pages : Flag that if set , collect all pages of content from the API when returning results .
 : type all _ pages : bool
 : param page : The page to show . Ignored if all _ pages is set .
 : type page : int
 : param query : Contains 2 - tuples used as query parameters to filter resulting resource objects .
 : type query : list
 : param ` * * kwargs ` : Keyword arguments list of available fields used for searching resource objects .
 : returns : A JSON object containing details of all resource objects returned by Tower backend .
 : rtype : dict
 : raises tower _ cli . exceptions . UsageError : When ` ` root ` ` flag is on and ` ` inventory ` ` is not present in
 ` ` * * kwargs ` ` .
 = = = = = API DOCS = = = = ="""

implementation:
# Option to list children of a parent group
if kwargs . get ( 'parent' , None ) :
    self . set_child_endpoint ( parent = kwargs [ 'parent' ] , inventory = kwargs . get ( 'inventory' , None ) )
    kwargs . pop ( 'parent' )
# Sanity check : If we got ` - - root ` and no inventory , that ' s an error .
if root and not kwargs . get ( 'inventory' , None ) :
    raise exc . UsageError ( 'The --root option requires specifying an inventory also.' )
# If we are tasked with getting root groups , do that .
if root :
    inventory_id = kwargs [ 'inventory' ]
    r = client . get ( '/inventories/%d/root_groups/' % inventory_id )
    return r . json ( )
# Return the superclass implementation .
return super ( Resource , self ) . list ( ** kwargs )


signature:
def associate ( self , group , parent , ** kwargs ) :
    """Associate this group with the specified group .
 = = = = = API DOCS = = = = =
 Associate this group with the specified group .
 : param group : Primary key or name of the child group to associate .
 : type group : str
 : param parent : Primary key or name of the parent group to associate to .
 : type parent : str
 : param inventory : Primary key or name of the inventory the association should happen in .
 : type inventory : str
 : returns : Dictionary of only one key " changed " , which indicates whether the association succeeded .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation:
parent_id = self . lookup_with_inventory ( parent , kwargs . get ( 'inventory' , None ) ) [ 'id' ]
group_id = self . lookup_with_inventory ( group , kwargs . get ( 'inventory' , None ) ) [ 'id' ]
return self . _assoc ( 'children' , parent_id , group_id )


signature:
def disassociate ( self , group , parent , ** kwargs ) :
    """Disassociate this group from the specified group .
 = = = = = API DOCS = = = = =
 Disassociate this group with the specified group .
 : param group : Primary key or name of the child group to disassociate .
 : type group : str
 : param parent : Primary key or name of the parent group to disassociate from .
 : type parent : str
 : param inventory : Primary key or name of the inventory the disassociation should happen in .
 : type inventory : str
 : returns : Dictionary of only one key " changed " , which indicates whether the disassociation succeeded .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation:
parent_id = self . lookup_with_inventory ( parent , kwargs . get ( 'inventory' , None ) ) [ 'id' ]
group_id = self . lookup_with_inventory ( group , kwargs . get ( 'inventory' , None ) ) [ 'id' ]
return self . _disassoc ( 'children' , parent_id , group_id )


signature:
def parse_kv ( var_string ) :
    """Similar to the Ansible function of the same name , parses file
 with a key = value pattern and stores information in a dictionary ,
 but not as fully featured as the corresponding Ansible code ."""

implementation:
return_dict = { }
# Output updates dictionaries , so return empty one if no vals in
if var_string is None :
    return { }
# Python 2.6 / shlex has problems handling unicode , this is a fix
fix_encoding_26 = False
if sys . version_info < ( 2 , 7 ) and '\x00' in shlex . split ( u'a' ) [ 0 ] :
    fix_encoding_26 = True
# Also hedge against Click library giving non - string type
is_unicode = False
if fix_encoding_26 or not isinstance ( var_string , str ) :
    if isinstance ( var_string , six . text_type ) :
        var_string = var_string . encode ( 'UTF-8' )
        is_unicode = True
    else :
        var_string = str ( var_string )
# Use shlex library to split string by quotes , whitespace , etc .
for token in shlex . split ( var_string ) : # Second part of fix to avoid passing shlex unicode in py2.6
    if ( is_unicode ) :
        token = token . decode ( 'UTF-8' )
    if fix_encoding_26 :
        token = six . text_type ( token )
    # Look for key = value pattern , if not , process as raw parameter
    if '=' in token :
        ( k , v ) = token . split ( '=' , 1 )
        # If ' = ' are unbalanced , then stop and warn user
        if len ( k ) == 0 or len ( v ) == 0 :
            raise Exception
        # If possible , convert into python data type , for instance " 5 " - > 5
        try :
            return_dict [ k ] = ast . literal_eval ( v )
        except Exception :
            return_dict [ k ] = v
    else : # scenario where - - extra - vars = 42 , will throw error
        raise Exception
return return_dict


signature:
def string_to_dict ( var_string , allow_kv = True , require_dict = True ) :
    """Returns a dictionary given a string with yaml or json syntax .
 If data is not present in a key : value format , then it return
 an empty dictionary .
 Attempts processing string by 3 different methods in order :
 1 . as JSON 2 . as YAML 3 . as custom key = value syntax
 Throws an error if all of these fail in the standard ways ."""

implementation:
# try :
# # Accept all valid " key " : value types of json
# return _ dict = json . loads ( var _ string )
# assert type ( return _ dict ) is dict
# except ( TypeError , AttributeError , ValueError , AssertionError ) :
try : # Accept all JSON and YAML
    return_dict = yaml . load ( var_string , Loader = yaml . SafeLoader )
    if require_dict :
        assert type ( return_dict ) is dict
except ( AttributeError , yaml . YAMLError , AssertionError ) : # if these fail , parse by key = value syntax
    try :
        assert allow_kv
        return_dict = parse_kv ( var_string )
    except Exception :
        raise exc . TowerCLIError ( 'failed to parse some of the extra ' 'variables.\nvariables: \n%s' % var_string )
return return_dict


signature:
def process_extra_vars ( extra_vars_list , force_json = True ) :
    """Returns a string that is valid JSON or YAML and contains all the
 variables in every extra _ vars _ opt inside of extra _ vars _ list .
 Args :
 parse _ kv ( bool ) : whether to allow key = value syntax .
 force _ json ( bool ) : if True , always output json ."""

implementation:
# Read from all the different sources and put into dictionary
extra_vars = { }
extra_vars_yaml = ""
for extra_vars_opt in extra_vars_list : # Load file content if necessary
    if extra_vars_opt . startswith ( "@" ) :
        with open ( extra_vars_opt [ 1 : ] , 'r' ) as f :
            extra_vars_opt = f . read ( )
        # Convert text markup to a dictionary conservatively
        opt_dict = string_to_dict ( extra_vars_opt , allow_kv = False )
    else : # Convert text markup to a dictionary liberally
        opt_dict = string_to_dict ( extra_vars_opt , allow_kv = True )
    # Rolling YAML - based string combination
    if any ( line . startswith ( "#" ) for line in extra_vars_opt . split ( '\n' ) ) :
        extra_vars_yaml += extra_vars_opt + "\n"
    elif extra_vars_opt != "" :
        extra_vars_yaml += yaml . dump ( opt_dict , default_flow_style = False ) + "\n"
    # Combine dictionary with cumulative dictionary
    extra_vars . update ( opt_dict )
# Return contents in form of a string
if not force_json :
    try : # Conditions to verify it is safe to return rolling YAML string
        try_dict = yaml . load ( extra_vars_yaml , Loader = yaml . SafeLoader )
        assert type ( try_dict ) is dict
        debug . log ( 'Using unprocessed YAML' , header = 'decision' , nl = 2 )
        return extra_vars_yaml . rstrip ( )
    except Exception :
        debug . log ( 'Failed YAML parsing, defaulting to JSON' , header = 'decison' , nl = 2 )
if extra_vars == { } :
    return ""
return json . dumps ( extra_vars , ensure_ascii = False )


signature:
def ordered_dump ( data , Dumper = yaml . Dumper , ** kws ) :
    """Expand PyYAML ' s built - in dumper to support parsing OrderedDict . Return
 a string as parse result of the original data structure , which includes
 OrderedDict .
 Args :
 data : the data structure to be dumped ( parsed ) which is supposed to
 contain OrderedDict .
 Dumper : the yaml serializer to be expanded and used .
 kws : extra key - value arguments to be passed to yaml . dump ."""

implementation:
class OrderedDumper ( Dumper ) :
    pass
def _dict_representer ( dumper , data ) :
    return dumper . represent_mapping ( yaml . resolver . BaseResolver . DEFAULT_MAPPING_TAG , data . items ( ) )
OrderedDumper . add_representer ( OrderedDict , _dict_representer )
return yaml . dump ( data , None , OrderedDumper , ** kws )


signature:
def get_prefix ( self , include_version = True ) :
    """Return the appropriate URL prefix to prepend to requests ,
 based on the host provided in settings ."""

implementation:
host = settings . host
if '://' not in host :
    host = 'https://%s' % host . strip ( '/' )
elif host . startswith ( 'http://' ) and settings . verify_ssl :
    raise exc . TowerCLIError ( 'Can not verify ssl with non-https protocol. Change the ' 'verify_ssl configuration setting to continue.' )
# Validate that we have either an http or https based URL
url_pieces = urlparse ( host )
if url_pieces [ 0 ] not in [ 'http' , 'https' ] :
    raise exc . ConnectionError ( 'URL must be http(s), {} is not valid' . format ( url_pieces [ 0 ] ) )
prefix = urljoin ( host , '/api/' )
if include_version : # We add the / to the end of { } so that our URL has the ending slash .
    prefix = urljoin ( prefix , "{}/" . format ( CUR_API_VERSION ) )
return prefix


signature:
def request ( self , method , url , * args , ** kwargs ) :
    """Make a request to the Ansible Tower API , and return the
 response ."""

implementation:
# If the URL has the api / vX at the front strip it off
# This is common to have if you are extracting a URL from an existing object .
# For example , any of the ' related ' fields of an object will have this
import re
url = re . sub ( "^/?api/v[0-9]+/" , "" , url )
# Piece together the full URL .
use_version = not url . startswith ( '/o/' )
url = '%s%s' % ( self . get_prefix ( use_version ) , url . lstrip ( '/' ) )
# Ansible Tower expects authenticated requests ; add the authentication
# from settings if it ' s provided .
kwargs . setdefault ( 'auth' , BasicTowerAuth ( settings . username , settings . password , self ) )
# POST and PUT requests will send JSON by default ; make this
# the content _ type by default . This makes it such that we don ' t have
# to constantly write that in our code , which gets repetitive .
headers = kwargs . get ( 'headers' , { } )
if method . upper ( ) in ( 'PATCH' , 'POST' , 'PUT' ) :
    headers . setdefault ( 'Content-Type' , 'application/json' )
    kwargs [ 'headers' ] = headers
# If debugging is on , print the URL and data being sent .
debug . log ( '%s %s' % ( method , url ) , fg = 'blue' , bold = True )
if method in ( 'POST' , 'PUT' , 'PATCH' ) :
    debug . log ( 'Data: %s' % kwargs . get ( 'data' , { } ) , fg = 'blue' , bold = True )
if method == 'GET' or kwargs . get ( 'params' , None ) :
    debug . log ( 'Params: %s' % kwargs . get ( 'params' , { } ) , fg = 'blue' , bold = True )
debug . log ( '' )
# If this is a JSON request , encode the data value .
if headers . get ( 'Content-Type' , '' ) == 'application/json' :
    kwargs [ 'data' ] = json . dumps ( kwargs . get ( 'data' , { } ) )
r = self . _make_request ( method , url , args , kwargs )
# Sanity check : Did the server send back some kind of internal error ?
# If so , bubble this up .
if r . status_code >= 500 :
    raise exc . ServerError ( 'The Tower server sent back a server error. ' 'Please try again later.' )
# Sanity check : Did we fail to authenticate properly ?
# If so , fail out now ; this is always a failure .
if r . status_code == 401 :
    raise exc . AuthError ( 'Invalid Tower authentication credentials (HTTP 401).' )
# Sanity check : Did we get a forbidden response , which means that
# the user isn ' t allowed to do this ? Report that .
if r . status_code == 403 :
    raise exc . Forbidden ( "You don't have permission to do that (HTTP 403)." )
# Sanity check : Did we get a 404 response ?
# Requests with primary keys will return a 404 if there is no response ,
# and we want to consistently trap these .
if r . status_code == 404 :
    raise exc . NotFound ( 'The requested object could not be found.' )
# Sanity check : Did we get a 405 response ?
# A 405 means we used a method that isn ' t allowed . Usually this
# is a bad request , but it requires special treatment because the
# API sends it as a logic error in a few situations ( e . g . trying to
# cancel a job that isn ' t running ) .
if r . status_code == 405 :
    raise exc . MethodNotAllowed ( "The Tower server says you can't make a request with the " "%s method to that URL (%s)." % ( method , url ) , )
# Sanity check : Did we get some other kind of error ?
# If so , write an appropriate error message .
if r . status_code >= 400 :
    raise exc . BadRequest ( 'The Tower server claims it was sent a bad request.\n\n' '%s %s\nParams: %s\nData: %s\n\nResponse: %s' % ( method , url , kwargs . get ( 'params' , None ) , kwargs . get ( 'data' , None ) , r . content . decode ( 'utf8' ) ) )
# Django REST Framework intelligently prints API keys in the
# order that they are defined in the models and serializer .
# We want to preserve this behavior when it is possible to do so
# with minimal effort , because while the order has no explicit meaning ,
# we make some effort to order keys in a convenient manner .
# To this end , make this response into an APIResponse subclass
# ( defined below ) , which has a ` json ` method that doesn ' t lose key
# order .
r . __class__ = APIResponse
# Return the response object .
return r


signature:
def _separate ( self , kwargs ) :
    """Remove None - valued and configuration - related keyworded arguments"""

implementation:
self . _pop_none ( kwargs )
result = { }
for field in Resource . config_fields :
    if field in kwargs :
        result [ field ] = kwargs . pop ( field )
        if field in Resource . json_fields : # If result [ field ] is not a string we can continue on
            if not isinstance ( result [ field ] , six . string_types ) :
                continue
            try :
                data = json . loads ( result [ field ] )
                result [ field ] = data
            except ValueError :
                raise exc . TowerCLIError ( 'Provided json file format ' 'invalid. Please recheck.' )
return result


signature:
def _configuration ( self , kwargs , config_item ) :
    """Combine configuration - related keyworded arguments into
 notification _ configuration ."""

implementation:
if 'notification_configuration' not in config_item :
    if 'notification_type' not in kwargs :
        return
    nc = kwargs [ 'notification_configuration' ] = { }
    for field in Resource . configuration [ kwargs [ 'notification_type' ] ] :
        if field not in config_item :
            raise exc . TowerCLIError ( 'Required config field %s not' ' provided.' % field )
        else :
            nc [ field ] = config_item [ field ]
else :
    kwargs [ 'notification_configuration' ] = config_item [ 'notification_configuration' ]


signature:
def create ( self , fail_on_found = False , force_on_exists = False , ** kwargs ) :
    """Create a notification template .
 All required configuration - related fields ( required according to
 notification _ type ) must be provided .
 There are two types of notification template creation : isolatedly
 creating a new notification template and creating a new notification
 template under a job template . Here the two types are discriminated by
 whether to provide - - job - template option . - - status option controls
 more specific , job - run - status - related association .
 Fields in the resource ' s ` identity ` tuple are used for a lookup ;
 if a match is found , then no - op ( unless ` force _ on _ exists ` is set ) but
 do not fail ( unless ` fail _ on _ found ` is set ) .
 = = = = = API DOCS = = = = =
 Create an object .
 : param fail _ on _ found : Flag that if set , the operation fails if an object matching the unique criteria
 already exists .
 : type fail _ on _ found : bool
 : param force _ on _ exists : Flag that if set , then if a match is found on unique fields , other fields will
 be updated to the provided values . ; If unset , a match causes the request to be
 a no - op .
 : type force _ on _ exists : bool
 : param ` * * kwargs ` : Keyword arguments which , all together , will be used as POST body to create the
 resource object .
 : returns : A dictionary combining the JSON output of the created resource , as well as two extra fields :
 " changed " , a flag indicating if the resource is created successfully ; " id " , an integer which
 is the primary key of the created object .
 : rtype : dict
 = = = = = API DOCS = = = = ="""

implementation:
config_item = self . _separate ( kwargs )
jt_id = kwargs . pop ( 'job_template' , None )
status = kwargs . pop ( 'status' , 'any' )
old_endpoint = self . endpoint
if jt_id is not None :
    jt = get_resource ( 'job_template' )
    jt . get ( pk = jt_id )
    try :
        nt_id = self . get ( ** copy . deepcopy ( kwargs ) ) [ 'id' ]
    except exc . NotFound :
        pass
    else :
        if fail_on_found :
            raise exc . TowerCLIError ( 'Notification template already ' 'exists and fail-on-found is ' 'switched on. Please use' ' "associate_notification" method' ' of job_template instead.' )
        else :
            debug . log ( 'Notification template already exists, ' 'associating with job template.' , header = 'details' )
            return jt . associate_notification_template ( jt_id , nt_id , status = status )
    self . endpoint = '/job_templates/%d/notification_templates_%s/' % ( jt_id , status )
self . _configuration ( kwargs , config_item )
result = super ( Resource , self ) . create ( ** kwargs )
self . endpoint = old_endpoint
return result