2015-04-23 05:41:05 +02:00
########################################################################
#
# (C) 2013, James Cammarata <jcammarata@ansible.com>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
########################################################################
2015-10-20 03:36:19 +02:00
from __future__ import ( absolute_import , division , print_function )
__metaclass__ = type
2015-04-23 05:41:05 +02:00
import os . path
import sys
import yaml
2015-12-09 16:51:12 +01:00
import time
2016-10-28 04:16:22 +02:00
import shutil
2015-04-23 05:41:05 +02:00
from collections import defaultdict
2016-10-28 04:16:22 +02:00
from jinja2 import Environment , FileSystemLoader
2015-04-23 05:41:05 +02:00
import ansible . constants as C
2015-05-01 03:22:23 +02:00
from ansible . cli import CLI
2015-04-27 13:31:41 +02:00
from ansible . errors import AnsibleError , AnsibleOptionsError
from ansible . galaxy import Galaxy
from ansible . galaxy . api import GalaxyAPI
from ansible . galaxy . role import GalaxyRole
2015-12-09 16:51:12 +01:00
from ansible . galaxy . login import GalaxyLogin
from ansible . galaxy . token import GalaxyToken
2015-04-30 05:55:44 +02:00
from ansible . playbook . role . requirement import RoleRequirement
2016-09-07 07:54:17 +02:00
from ansible . module_utils . _text import to_bytes , to_text
2015-08-22 08:28:03 +02:00
2015-11-10 20:40:55 +01:00
try :
from __main__ import display
except ImportError :
from ansible . utils . display import Display
display = Display ( )
2016-09-07 07:54:17 +02:00
2015-04-27 13:31:41 +02:00
class GalaxyCLI ( CLI ) :
2015-04-23 05:41:05 +02:00
2015-12-09 16:51:12 +01:00
SKIP_INFO_KEYS = ( " name " , " description " , " readme_html " , " related " , " summary_fields " , " average_aw_composite " , " average_aw_score " , " url " )
2015-12-10 16:57:48 +01:00
VALID_ACTIONS = ( " delete " , " import " , " info " , " init " , " install " , " list " , " login " , " remove " , " search " , " setup " )
2016-04-29 05:28:02 +02:00
2015-11-10 20:40:55 +01:00
def __init__ ( self , args ) :
2015-04-27 13:31:41 +02:00
self . api = None
self . galaxy = None
2015-11-10 20:40:55 +01:00
super ( GalaxyCLI , self ) . __init__ ( args )
2015-04-23 05:41:05 +02:00
def parse ( self ) :
''' create an options parser for bin/ansible '''
2015-04-27 13:31:41 +02:00
2015-05-01 03:22:23 +02:00
self . parser = CLI . base_parser (
usage = " usage: %% prog [ %s ] [--help] [options] ... " % " | " . join ( self . VALID_ACTIONS ) ,
epilog = " \n See ' %s <command> --help ' for more information on a specific command. \n \n " % os . path . basename ( sys . argv [ 0 ] )
)
2015-12-12 19:43:10 +01:00
2015-05-01 03:22:23 +02:00
self . set_action ( )
2015-04-30 05:55:44 +02:00
2016-05-31 15:30:50 +02:00
# common
self . parser . add_option ( ' -s ' , ' --server ' , dest = ' api_server ' , default = C . GALAXY_SERVER , help = ' The API server destination ' )
self . parser . add_option ( ' -c ' , ' --ignore-certs ' , action = ' store_true ' , dest = ' ignore_certs ' , default = C . GALAXY_IGNORE_CERTS , help = ' Ignore SSL certificate validation errors. ' )
# specific to actions
2015-12-09 16:51:12 +01:00
if self . action == " delete " :
self . parser . set_usage ( " usage: % prog delete [options] github_user github_repo " )
elif self . action == " import " :
self . parser . set_usage ( " usage: % prog import [options] github_user github_repo " )
2016-05-31 15:30:50 +02:00
self . parser . add_option ( ' --no-wait ' , dest = ' wait ' , action = ' store_false ' , default = True , help = ' Don \' t wait for import results. ' )
self . parser . add_option ( ' --branch ' , dest = ' reference ' , help = ' The name of a branch to import. Defaults to the repository \' s default branch (usually master) ' )
2016-10-28 04:34:59 +02:00
self . parser . add_option ( ' --role-name ' , dest = ' role_name ' , help = ' The name the role should have, if different than the repo name ' )
2016-05-31 15:30:50 +02:00
self . parser . add_option ( ' --status ' , dest = ' check_status ' , action = ' store_true ' , default = False , help = ' Check the status of the most recent import request for given github_user/github_repo. ' )
2015-12-09 16:51:12 +01:00
elif self . action == " info " :
2015-08-22 08:28:03 +02:00
self . parser . set_usage ( " usage: % prog info [options] role_name[,version] " )
2015-04-23 05:41:05 +02:00
elif self . action == " init " :
2015-08-22 08:28:03 +02:00
self . parser . set_usage ( " usage: % prog init [options] role_name " )
2016-05-31 15:30:50 +02:00
self . parser . add_option ( ' -p ' , ' --init-path ' , dest = ' init_path ' , default = " ./ " , help = ' The path in which the skeleton role will be created. The default is the current working directory. ' )
2016-10-28 04:16:22 +02:00
self . parser . add_option ( ' --container-enabled ' , dest = ' container_enabled ' , action = ' store_true ' , default = False ,
help = ' Initialize the skeleton role with default contents for a Container Enabled role. ' )
2015-04-23 05:41:05 +02:00
elif self . action == " install " :
2015-08-22 08:28:03 +02:00
self . parser . set_usage ( " usage: % prog install [options] [-r FILE | role_name(s)[,version] | scm+role_repo_url[,version] | tar_file(s)] " )
2016-05-31 15:30:50 +02:00
self . parser . add_option ( ' -i ' , ' --ignore-errors ' , dest = ' ignore_errors ' , action = ' store_true ' , default = False , help = ' Ignore errors and continue with the next specified role. ' )
self . parser . add_option ( ' -n ' , ' --no-deps ' , dest = ' no_deps ' , action = ' store_true ' , default = False , help = ' Don \' t download roles listed as dependencies ' )
self . parser . add_option ( ' -r ' , ' --role-file ' , dest = ' role_file ' , help = ' A file containing a list of roles to be imported ' )
2015-04-23 05:41:05 +02:00
elif self . action == " remove " :
2015-08-22 08:28:03 +02:00
self . parser . set_usage ( " usage: % prog remove role1 role2 ... " )
2015-04-23 05:41:05 +02:00
elif self . action == " list " :
2015-08-22 08:28:03 +02:00
self . parser . set_usage ( " usage: % prog list [role_name] " )
2015-12-09 16:51:12 +01:00
elif self . action == " login " :
self . parser . set_usage ( " usage: % prog login [options] " )
2016-05-31 15:30:50 +02:00
self . parser . add_option ( ' --github-token ' , dest = ' token ' , default = None , help = ' Identify with github token rather than username and password. ' )
2015-08-22 08:28:03 +02:00
elif self . action == " search " :
2015-12-09 16:51:12 +01:00
self . parser . set_usage ( " usage: % prog search [searchterm1 searchterm2] [--galaxy-tags galaxy_tag1,galaxy_tag2] [--platforms platform1,platform2] [--author username] " )
2016-05-31 15:30:50 +02:00
self . parser . add_option ( ' --platforms ' , dest = ' platforms ' , help = ' list of OS platforms to filter by ' )
2016-09-29 23:14:02 +02:00
self . parser . add_option ( ' --galaxy-tags ' , dest = ' galaxy_tags ' , help = ' list of galaxy tags to filter by ' )
2016-05-31 15:30:50 +02:00
self . parser . add_option ( ' --author ' , dest = ' author ' , help = ' GitHub username ' )
2015-12-09 16:51:12 +01:00
elif self . action == " setup " :
2015-12-09 23:09:34 +01:00
self . parser . set_usage ( " usage: % prog setup [options] source github_user github_repo secret " )
2016-05-31 15:30:50 +02:00
self . parser . add_option ( ' --remove ' , dest = ' remove_id ' , default = None , help = ' Remove the integration matching the provided ID value. Use --list to see ID values. ' )
self . parser . add_option ( ' --list ' , dest = " setup_list " , action = ' store_true ' , default = False , help = ' List all of your integrations. ' )
2015-04-23 05:41:05 +02:00
# options that apply to more than one action
2016-05-31 15:30:50 +02:00
if self . action in [ ' init ' , ' info ' ] :
self . parser . add_option ( ' --offline ' , dest = ' offline ' , default = False , action = ' store_true ' , help = " Don ' t query the galaxy API when creating roles " )
2016-09-07 07:54:17 +02:00
if self . action not in ( " delete " , " import " , " init " , " login " , " setup " ) :
2016-04-03 02:22:00 +02:00
# NOTE: while the option type=str, the default is a list, and the
# callback will set the value to a list.
2016-05-31 15:30:50 +02:00
self . parser . add_option ( ' -p ' , ' --roles-path ' , dest = ' roles_path ' , action = " callback " , callback = CLI . expand_paths , type = str , default = C . DEFAULT_ROLES_PATH ,
help = ' The path to the directory containing your roles. The default is the roles_path configured in your ansible.cfg file (/etc/ansible/roles if not configured) ' )
2015-04-23 05:41:05 +02:00
if self . action in ( " init " , " install " ) :
2016-05-31 15:30:50 +02:00
self . parser . add_option ( ' -f ' , ' --force ' , dest = ' force ' , action = ' store_true ' , default = False , help = ' Force overwriting an existing role ' )
2015-04-23 05:41:05 +02:00
2016-09-29 23:14:02 +02:00
super ( GalaxyCLI , self ) . parse ( )
2015-12-09 23:09:34 +01:00
display . verbosity = self . options . verbosity
self . galaxy = Galaxy ( self . options )
2015-04-23 05:41:05 +02:00
2015-04-27 13:31:41 +02:00
def run ( self ) :
2015-07-04 16:23:30 +02:00
2016-04-29 05:28:02 +02:00
super ( GalaxyCLI , self ) . run ( )
2015-04-23 05:41:05 +02:00
2016-04-29 05:28:02 +02:00
self . api = GalaxyAPI ( self . galaxy )
2015-04-27 13:31:41 +02:00
self . execute ( )
2015-04-23 05:41:05 +02:00
2015-04-27 13:31:41 +02:00
def exit_without_ignore ( self , rc = 1 ) :
2015-04-23 05:41:05 +02:00
"""
Exits with the specified return code unless the
option - - ignore - errors was specified
"""
2015-04-27 13:31:41 +02:00
if not self . get_opt ( " ignore_errors " , False ) :
2015-07-30 02:13:17 +02:00
raise AnsibleError ( ' - you can use --ignore-errors to skip failed roles and finish processing the list. ' )
2015-04-23 05:41:05 +02:00
2015-08-22 08:28:03 +02:00
def _display_role_info ( self , role_info ) :
2016-09-07 07:54:17 +02:00
text = [ u " " , u " Role: %s " % to_text ( role_info [ ' name ' ] ) ]
2016-01-28 19:50:20 +01:00
text . append ( u " \t description: %s " % role_info . get ( ' description ' , ' ' ) )
2015-08-22 08:28:03 +02:00
for k in sorted ( role_info . keys ( ) ) :
if k in self . SKIP_INFO_KEYS :
continue
if isinstance ( role_info [ k ] , dict ) :
2016-01-28 19:50:20 +01:00
text . append ( u " \t %s : " % ( k ) )
2015-08-22 08:28:03 +02:00
for key in sorted ( role_info [ k ] . keys ( ) ) :
if key in self . SKIP_INFO_KEYS :
continue
2016-01-28 19:50:20 +01:00
text . append ( u " \t \t %s : %s " % ( key , role_info [ k ] [ key ] ) )
2015-08-22 08:28:03 +02:00
else :
2016-01-28 19:50:20 +01:00
text . append ( u " \t %s : %s " % ( k , role_info [ k ] ) )
2015-08-22 08:28:03 +02:00
2016-01-28 19:50:20 +01:00
return u ' \n ' . join ( text )
2015-08-22 08:28:03 +02:00
############################
# execute actions
############################
2015-04-27 13:31:41 +02:00
def execute_init ( self ) :
2015-04-23 05:41:05 +02:00
"""
Executes the init action , which creates the skeleton framework
of a role that complies with the galaxy metadata format .
"""
2015-04-27 13:31:41 +02:00
init_path = self . get_opt ( ' init_path ' , ' ./ ' )
force = self . get_opt ( ' force ' , False )
offline = self . get_opt ( ' offline ' , False )
2015-10-07 11:37:23 +02:00
role_name = self . args . pop ( 0 ) . strip ( ) if self . args else None
if not role_name :
2015-04-27 13:31:41 +02:00
raise AnsibleOptionsError ( " - no role name specified for init " )
role_path = os . path . join ( init_path , role_name )
if os . path . exists ( role_path ) :
if os . path . isfile ( role_path ) :
raise AnsibleError ( " - the path %s already exists, but is a file - aborting " % role_path )
elif not force :
2015-11-10 20:40:55 +01:00
raise AnsibleError ( " - the directory %s already exists. "
" you can use --force to re-initialize this directory, \n "
" however it will reset any main.yml files that may have \n "
" been modified there already. " % role_path )
2015-04-23 05:41:05 +02:00
2016-10-28 04:16:22 +02:00
platforms = [ ]
if not offline :
platforms = self . api . get_list ( " platforms " ) or [ ]
# group the list of platforms from the api based
# on their names, with the release field being
# appended to a list of versions
platform_groups = defaultdict ( list )
for platform in platforms :
platform_groups [ platform [ ' name ' ] ] . append ( platform [ ' release ' ] )
platform_groups [ platform [ ' name ' ] ] . sort ( )
inject_data = dict (
role_name = role_name ,
author = ' your name ' ,
description = ' your description ' ,
company = ' your company (optional) ' ,
license = ' license (GPLv2, CC-BY, etc) ' ,
issue_tracker_url = ' http://example.com/issue/tracker ' ,
min_ansible_version = ' 1.2 ' ,
platforms = platform_groups ,
container_enabled = self . options . container_enabled
)
# create role directory
2015-04-23 05:41:05 +02:00
if not os . path . exists ( role_path ) :
os . makedirs ( role_path )
2016-10-28 04:16:22 +02:00
role_skeleton = self . galaxy . default_role_skeleton_path
role_skeleton = os . path . expanduser ( role_skeleton )
template_env = Environment ( loader = FileSystemLoader ( role_skeleton ) )
for root , dirs , files in os . walk ( role_skeleton , topdown = True ) :
rel_root = os . path . relpath ( root , role_skeleton )
in_templates_dir = rel_root . split ( os . sep , 1 ) [ 0 ] == ' templates '
for f in files :
filename , ext = os . path . splitext ( f )
if ext == " .j2 " and not in_templates_dir :
src_template = os . path . join ( rel_root , f )
dest_file = os . path . join ( role_path , rel_root , filename )
template_env . get_template ( src_template ) . stream ( inject_data ) . dump ( dest_file )
else :
f_rel_path = os . path . relpath ( os . path . join ( root , f ) , role_skeleton )
shutil . copyfile ( os . path . join ( root , f ) , os . path . join ( role_path , f_rel_path ) )
for d in dirs :
dir_path = os . path . join ( role_path , rel_root , d )
if not os . path . exists ( dir_path ) :
os . makedirs ( dir_path )
2015-11-10 20:40:55 +01:00
display . display ( " - %s was created successfully " % role_name )
2015-04-23 05:41:05 +02:00
2015-04-27 13:31:41 +02:00
def execute_info ( self ) :
2015-04-23 05:41:05 +02:00
"""
Executes the info action . This action prints out detailed
information about an installed role as well as info available
from the galaxy API .
"""
2015-04-27 13:31:41 +02:00
if len ( self . args ) == 0 :
2015-04-23 05:41:05 +02:00
# the user needs to specify a role
2015-04-27 13:31:41 +02:00
raise AnsibleOptionsError ( " - you must specify a user/role name " )
2015-04-23 05:41:05 +02:00
2015-04-27 13:31:41 +02:00
roles_path = self . get_opt ( " roles_path " )
2015-04-23 05:41:05 +02:00
2015-08-22 08:28:03 +02:00
data = ' '
2015-04-27 13:31:41 +02:00
for role in self . args :
2015-04-23 05:41:05 +02:00
2015-10-06 04:59:08 +02:00
role_info = { ' path ' : roles_path }
2015-04-30 05:55:44 +02:00
gr = GalaxyRole ( self . galaxy , role )
2015-04-23 05:41:05 +02:00
2015-04-30 05:55:44 +02:00
install_info = gr . install_info
2015-04-23 05:41:05 +02:00
if install_info :
if ' version ' in install_info :
install_info [ ' intalled_version ' ] = install_info [ ' version ' ]
del install_info [ ' version ' ]
role_info . update ( install_info )
2015-04-30 05:55:44 +02:00
remote_data = False
2016-04-29 05:28:02 +02:00
if not self . options . offline :
2015-04-30 05:55:44 +02:00
remote_data = self . api . lookup_role_by_name ( role , False )
2015-04-23 05:41:05 +02:00
if remote_data :
role_info . update ( remote_data )
2015-04-30 05:55:44 +02:00
if gr . metadata :
role_info . update ( gr . metadata )
2015-04-23 05:41:05 +02:00
2015-04-30 05:55:44 +02:00
req = RoleRequirement ( )
2015-10-06 04:59:08 +02:00
role_spec = req . role_yaml_parse ( { ' role ' : role } )
2015-04-23 05:41:05 +02:00
if role_spec :
role_info . update ( role_spec )
2016-01-28 19:50:20 +01:00
data = self . _display_role_info ( role_info )
### FIXME: This is broken in both 1.9 and 2.0 as
# _display_role_info() always returns something
2015-08-22 08:42:21 +02:00
if not data :
2016-01-28 19:50:20 +01:00
data = u " \n - the role %s was not found " % role
2015-08-22 08:42:21 +02:00
2015-08-22 08:28:03 +02:00
self . pager ( data )
2015-04-23 05:41:05 +02:00
2015-04-27 13:31:41 +02:00
def execute_install ( self ) :
2015-04-23 05:41:05 +02:00
"""
Executes the installation action . The args list contains the
roles to be installed , unless - f was specified . The list of roles
can be a name ( which will be downloaded via the galaxy API and github ) ,
or it can be a local . tar . gz file .
"""
2015-04-27 13:31:41 +02:00
role_file = self . get_opt ( " role_file " , None )
2015-04-23 05:41:05 +02:00
2015-04-27 13:31:41 +02:00
if len ( self . args ) == 0 and role_file is None :
2015-04-23 05:41:05 +02:00
# the user needs to specify one of either --role-file
# or specify a single user/role name
2015-04-27 13:31:41 +02:00
raise AnsibleOptionsError ( " - you must specify a user/role name or a roles file " )
2015-11-10 20:40:55 +01:00
elif len ( self . args ) == 1 and role_file is not None :
2015-04-23 05:41:05 +02:00
# using a role file is mutually exclusive of specifying
# the role name on the command line
2015-04-27 13:31:41 +02:00
raise AnsibleOptionsError ( " - please specify a user/role name, or a roles file, but not both " )
2015-04-23 05:41:05 +02:00
2015-04-27 13:31:41 +02:00
no_deps = self . get_opt ( " no_deps " , False )
2015-09-15 23:30:36 +02:00
force = self . get_opt ( ' force ' , False )
2015-04-23 05:41:05 +02:00
2015-04-30 05:55:44 +02:00
roles_left = [ ]
2015-04-23 05:41:05 +02:00
if role_file :
2015-07-30 02:13:17 +02:00
try :
f = open ( role_file , ' r ' )
if role_file . endswith ( ' .yaml ' ) or role_file . endswith ( ' .yml ' ) :
2015-10-20 20:38:07 +02:00
try :
2016-09-07 07:54:17 +02:00
required_roles = yaml . safe_load ( f . read ( ) )
2015-10-20 20:38:07 +02:00
except Exception as e :
raise AnsibleError ( " Unable to load data from the requirements file: %s " % role_file )
if required_roles is None :
raise AnsibleError ( " No roles found in file: %s " % role_file )
for role in required_roles :
2015-10-23 20:17:33 +02:00
if " include " not in role :
role = RoleRequirement . role_yaml_parse ( role )
display . vvv ( " found role %s in yaml file " % str ( role ) )
if " name " not in role and " scm " not in role :
raise AnsibleError ( " Must specify name or src for role " )
roles_left . append ( GalaxyRole ( self . galaxy , * * role ) )
else :
with open ( role [ " include " ] ) as f_include :
try :
roles_left + = [
GalaxyRole ( self . galaxy , * * r ) for r in
map ( RoleRequirement . role_yaml_parse ,
yaml . safe_load ( f_include ) )
]
except Exception as e :
msg = " Unable to load data from the include requirements file: %s %s "
raise AnsibleError ( msg % ( role_file , e ) )
2015-07-30 02:13:17 +02:00
else :
2015-11-10 20:40:55 +01:00
display . deprecated ( " going forward only the yaml format will be supported " )
2015-07-30 02:13:17 +02:00
# roles listed in a file, one per line
2015-10-03 16:29:28 +02:00
for rline in f . readlines ( ) :
2015-11-04 06:31:11 +01:00
if rline . startswith ( " # " ) or rline . strip ( ) == ' ' :
continue
2015-11-10 20:40:55 +01:00
display . debug ( ' found role %s in text file ' % str ( rline ) )
2015-10-07 03:25:28 +02:00
role = RoleRequirement . role_yaml_parse ( rline . strip ( ) )
2015-10-06 07:54:48 +02:00
roles_left . append ( GalaxyRole ( self . galaxy , * * role ) )
2015-07-30 02:13:17 +02:00
f . close ( )
2015-10-03 16:29:28 +02:00
except ( IOError , OSError ) as e :
2015-11-10 20:40:55 +01:00
display . error ( ' Unable to open %s : %s ' % ( role_file , str ( e ) ) )
2015-04-23 05:41:05 +02:00
else :
# roles were specified directly, so we'll just go out grab them
# (and their dependencies, unless the user doesn't want us to).
2015-04-30 05:55:44 +02:00
for rname in self . args :
2016-01-21 13:00:29 +01:00
role = RoleRequirement . role_yaml_parse ( rname . strip ( ) )
roles_left . append ( GalaxyRole ( self . galaxy , * * role ) )
2015-04-23 05:41:05 +02:00
2015-10-03 16:29:28 +02:00
for role in roles_left :
2015-12-09 16:51:12 +01:00
display . vvv ( ' Installing role %s ' % role . name )
2015-04-23 05:41:05 +02:00
# query the galaxy API for the role data
2015-09-15 23:30:36 +02:00
if role . install_info is not None and not force :
2015-11-10 20:40:55 +01:00
display . display ( ' - %s is already installed, skipping. ' % role . name )
2015-09-15 23:30:36 +02:00
continue
2015-07-30 18:34:57 +02:00
2015-10-05 19:03:34 +02:00
try :
installed = role . install ( )
except AnsibleError as e :
2015-11-10 20:40:55 +01:00
display . warning ( " - %s was NOT installed successfully: %s " % ( role . name , str ( e ) ) )
2015-10-05 19:03:34 +02:00
self . exit_without_ignore ( )
continue
# install dependencies, if we want them
if not no_deps and installed :
2015-10-07 21:41:11 +02:00
role_dependencies = role . metadata . get ( ' dependencies ' ) or [ ]
2015-10-05 19:03:34 +02:00
for dep in role_dependencies :
2015-11-10 20:40:55 +01:00
display . debug ( ' Installing dep %s ' % dep )
2015-10-05 19:03:34 +02:00
dep_req = RoleRequirement ( )
2015-10-06 04:59:08 +02:00
dep_info = dep_req . role_yaml_parse ( dep )
dep_role = GalaxyRole ( self . galaxy , * * dep_info )
if ' . ' not in dep_role . name and ' . ' not in dep_role . src and dep_role . scm is None :
# we know we can skip this, as it's not going to
# be found on galaxy.ansible.com
continue
2015-10-05 19:03:34 +02:00
if dep_role . install_info is None or force :
if dep_role not in roles_left :
2015-11-10 20:40:55 +01:00
display . display ( ' - adding dependency: %s ' % dep_role . name )
2015-10-06 04:59:08 +02:00
roles_left . append ( dep_role )
2015-07-30 18:34:57 +02:00
else :
2015-11-10 20:40:55 +01:00
display . display ( ' - dependency %s already pending installation. ' % dep_role . name )
2015-10-05 19:03:34 +02:00
else :
2015-11-10 20:40:55 +01:00
display . display ( ' - dependency %s is already installed, skipping. ' % dep_role . name )
2015-04-30 05:55:44 +02:00
2015-10-03 16:29:28 +02:00
if not installed :
2015-11-10 20:40:55 +01:00
display . warning ( " - %s was NOT installed successfully. " % role . name )
2015-04-30 05:55:44 +02:00
self . exit_without_ignore ( )
2015-10-03 16:29:28 +02:00
2015-04-27 13:31:41 +02:00
return 0
2015-04-23 05:41:05 +02:00
2015-04-27 13:31:41 +02:00
def execute_remove ( self ) :
2015-04-23 05:41:05 +02:00
"""
Executes the remove action . The args list contains the list
of roles to be removed . This list can contain more than one role .
"""
2015-04-27 13:31:41 +02:00
if len ( self . args ) == 0 :
raise AnsibleOptionsError ( ' - you must specify at least one role to remove. ' )
2015-04-23 05:41:05 +02:00
2015-04-30 05:55:44 +02:00
for role_name in self . args :
role = GalaxyRole ( self . galaxy , role_name )
try :
if role . remove ( ) :
2015-11-10 20:40:55 +01:00
display . display ( ' - successfully removed %s ' % role_name )
2015-04-23 05:41:05 +02:00
else :
2015-11-10 20:40:55 +01:00
display . display ( ' - %s is not installed, skipping. ' % role_name )
2015-04-30 05:55:44 +02:00
except Exception as e :
raise AnsibleError ( " Failed to remove role %s : %s " % ( role_name , str ( e ) ) )
2015-04-27 13:31:41 +02:00
return 0
2015-04-23 05:41:05 +02:00
2015-04-27 13:31:41 +02:00
def execute_list ( self ) :
2015-04-23 05:41:05 +02:00
"""
Executes the list action . The args list can contain zero
or one role . If one is specified , only that role will be
shown , otherwise all roles in the specified directory will
be shown .
"""
2015-04-27 13:31:41 +02:00
if len ( self . args ) > 1 :
raise AnsibleOptionsError ( " - please specify only one role to list, or specify no roles to see a full list " )
2015-04-23 05:41:05 +02:00
2015-04-27 13:31:41 +02:00
if len ( self . args ) == 1 :
2015-04-23 05:41:05 +02:00
# show only the request role, if it exists
2015-07-24 22:18:55 +02:00
name = self . args . pop ( )
gr = GalaxyRole ( self . galaxy , name )
2015-04-30 05:55:44 +02:00
if gr . metadata :
install_info = gr . install_info
2015-04-23 05:41:05 +02:00
version = None
if install_info :
version = install_info . get ( " version " , None )
if not version :
version = " (unknown version) "
# show some more info about single roles here
2015-11-10 20:40:55 +01:00
display . display ( " - %s , %s " % ( name , version ) )
2015-04-23 05:41:05 +02:00
else :
2015-11-10 20:40:55 +01:00
display . display ( " - the role %s was not found " % name )
2015-04-23 05:41:05 +02:00
else :
# show all valid roles in the roles_path directory
2015-04-27 13:31:41 +02:00
roles_path = self . get_opt ( ' roles_path ' )
2016-05-31 15:00:06 +02:00
for path in roles_path :
role_path = os . path . expanduser ( path )
if not os . path . exists ( role_path ) :
2016-06-03 15:46:51 +02:00
raise AnsibleOptionsError ( " - the path %s does not exist. Please specify a valid path with --roles-path " % role_path )
2016-05-31 15:00:06 +02:00
elif not os . path . isdir ( role_path ) :
2016-06-03 15:46:51 +02:00
raise AnsibleOptionsError ( " - %s exists, but it is not a directory. Please specify a valid path with --roles-path " % role_path )
2016-05-31 15:00:06 +02:00
path_files = os . listdir ( role_path )
for path_file in path_files :
gr = GalaxyRole ( self . galaxy , path_file )
if gr . metadata :
install_info = gr . install_info
version = None
if install_info :
version = install_info . get ( " version " , None )
if not version :
version = " (unknown version) "
display . display ( " - %s , %s " % ( path_file , version ) )
2015-04-27 13:31:41 +02:00
return 0
2015-07-29 16:58:46 +02:00
2015-08-22 08:28:03 +02:00
def execute_search ( self ) :
2015-12-09 16:51:12 +01:00
page_size = 1000
2015-08-22 08:28:03 +02:00
search = None
2015-12-12 19:43:10 +01:00
2015-12-09 16:51:12 +01:00
if len ( self . args ) :
terms = [ ]
for i in range ( len ( self . args ) ) :
2016-09-07 07:54:17 +02:00
terms . append ( self . args . pop ( ) )
2015-12-10 04:56:54 +01:00
search = ' + ' . join ( terms [ : : - 1 ] )
2015-12-09 16:51:12 +01:00
2016-09-29 23:14:02 +02:00
if not search and not self . options . platforms and not self . options . galaxy_tags and not self . options . author :
2015-12-09 16:51:12 +01:00
raise AnsibleError ( " Invalid query. At least one search term, platform, galaxy tag or author must be provided. " )
response = self . api . search_roles ( search , platforms = self . options . platforms ,
2016-09-29 23:14:02 +02:00
tags = self . options . galaxy_tags , author = self . options . author , page_size = page_size )
2015-12-12 19:43:10 +01:00
2015-12-09 16:51:12 +01:00
if response [ ' count ' ] == 0 :
2015-12-29 21:41:00 +01:00
display . display ( " No roles match your search. " , color = C . COLOR_ERROR )
2015-12-09 16:51:12 +01:00
return True
2015-07-29 16:58:46 +02:00
2016-01-28 19:50:20 +01:00
data = [ u ' ' ]
2015-07-29 16:58:46 +02:00
2015-12-09 16:51:12 +01:00
if response [ ' count ' ] > page_size :
2016-01-28 19:50:20 +01:00
data . append ( u " Found %d roles matching your search. Showing first %s . " % ( response [ ' count ' ] , page_size ) )
2015-12-09 16:51:12 +01:00
else :
2016-01-28 19:50:20 +01:00
data . append ( u " Found %d roles matching your search: " % response [ ' count ' ] )
2015-12-09 16:51:12 +01:00
max_len = [ ]
for role in response [ ' results ' ] :
max_len . append ( len ( role [ ' username ' ] + ' . ' + role [ ' name ' ] ) )
name_len = max ( max_len )
2016-01-28 19:50:20 +01:00
format_str = u " %% - %d s %% s " % name_len
data . append ( u ' ' )
data . append ( format_str % ( u " Name " , u " Description " ) )
data . append ( format_str % ( u " ---- " , u " ----------- " ) )
2015-12-09 16:51:12 +01:00
for role in response [ ' results ' ] :
2016-01-28 19:50:20 +01:00
data . append ( format_str % ( u ' %s . %s ' % ( role [ ' username ' ] , role [ ' name ' ] ) , role [ ' description ' ] ) )
2015-12-12 19:43:10 +01:00
2016-01-28 19:50:20 +01:00
data = u ' \n ' . join ( data )
2015-08-22 08:28:03 +02:00
self . pager ( data )
2015-12-09 16:51:12 +01:00
return True
def execute_login ( self ) :
"""
2016-12-11 03:50:09 +01:00
Verify user ' s identify via Github and retrieve an auth token from Galaxy.
2015-12-09 16:51:12 +01:00
"""
# Authenticate with github and retrieve a token
if self . options . token is None :
login = GalaxyLogin ( self . galaxy )
github_token = login . create_github_token ( )
else :
github_token = self . options . token
galaxy_response = self . api . authenticate ( github_token )
2015-12-12 19:43:10 +01:00
2015-12-09 16:51:12 +01:00
if self . options . token is None :
# Remove the token we created
login . remove_github_token ( )
2015-12-12 19:43:10 +01:00
# Store the Galaxy token
2015-12-09 16:51:12 +01:00
token = GalaxyToken ( )
token . set ( galaxy_response [ ' token ' ] )
2016-12-11 03:50:09 +01:00
display . display ( " Successfully logged into Galaxy as %s " % galaxy_response [ ' username ' ] )
2015-12-09 16:51:12 +01:00
return 0
def execute_import ( self ) :
"""
Import a role into Galaxy
"""
2015-12-12 19:43:10 +01:00
2015-12-09 16:51:12 +01:00
colors = {
' INFO ' : ' normal ' ,
2015-12-29 21:41:00 +01:00
' WARNING ' : C . COLOR_WARN ,
' ERROR ' : C . COLOR_ERROR ,
' SUCCESS ' : C . COLOR_OK ,
' FAILED ' : C . COLOR_ERROR ,
2015-12-09 16:51:12 +01:00
}
if len ( self . args ) < 2 :
raise AnsibleError ( " Expected a github_username and github_repository. Use --help. " )
2016-09-07 07:54:17 +02:00
github_repo = to_text ( self . args . pop ( ) , errors = ' surrogate_or_strict ' )
github_user = to_text ( self . args . pop ( ) , errors = ' surrogate_or_strict ' )
2015-12-09 16:51:12 +01:00
if self . options . check_status :
task = self . api . get_import_task ( github_user = github_user , github_repo = github_repo )
else :
# Submit an import request
2016-10-28 04:34:59 +02:00
task = self . api . create_import_task ( github_user , github_repo , reference = self . options . reference , role_name = self . options . role_name )
2015-12-12 19:43:10 +01:00
2015-12-09 16:51:12 +01:00
if len ( task ) > 1 :
# found multiple roles associated with github_user/github_repo
display . display ( " WARNING: More than one Galaxy role associated with Github repo %s / %s . " % ( github_user , github_repo ) ,
color = ' yellow ' )
2015-12-29 21:41:00 +01:00
display . display ( " The following Galaxy roles are being updated: " + u ' \n ' , color = C . COLOR_CHANGED )
2015-12-09 16:51:12 +01:00
for t in task :
2015-12-29 21:41:00 +01:00
display . display ( ' %s . %s ' % ( t [ ' summary_fields ' ] [ ' role ' ] [ ' namespace ' ] , t [ ' summary_fields ' ] [ ' role ' ] [ ' name ' ] ) , color = C . COLOR_CHANGED )
2016-09-07 07:54:17 +02:00
display . display ( u ' \n To properly namespace this role, remove each of the above and re-import %s / %s from scratch ' % ( github_user , github_repo ) ,
color = C . COLOR_CHANGED )
2015-12-09 16:51:12 +01:00
return 0
# found a single role as expected
display . display ( " Successfully submitted import request %d " % task [ 0 ] [ ' id ' ] )
if not self . options . wait :
display . display ( " Role name: %s " % task [ 0 ] [ ' summary_fields ' ] [ ' role ' ] [ ' name ' ] )
display . display ( " Repo: %s / %s " % ( task [ 0 ] [ ' github_user ' ] , task [ 0 ] [ ' github_repo ' ] ) )
if self . options . check_status or self . options . wait :
# Get the status of the import
msg_list = [ ]
finished = False
while not finished :
task = self . api . get_import_task ( task_id = task [ 0 ] [ ' id ' ] )
for msg in task [ 0 ] [ ' summary_fields ' ] [ ' task_messages ' ] :
if msg [ ' id ' ] not in msg_list :
display . display ( msg [ ' message_text ' ] , color = colors [ msg [ ' message_type ' ] ] )
msg_list . append ( msg [ ' id ' ] )
if task [ 0 ] [ ' state ' ] in [ ' SUCCESS ' , ' FAILED ' ] :
finished = True
else :
time . sleep ( 10 )
return 0
def execute_setup ( self ) :
"""
Setup an integration from Github or Travis
"""
if self . options . setup_list :
# List existing integration secrets
secrets = self . api . list_secrets ( )
if len ( secrets ) == 0 :
# None found
display . display ( " No integrations found. " )
return 0
2015-12-29 21:41:00 +01:00
display . display ( u ' \n ' + " ID Source Repo " , color = C . COLOR_OK )
display . display ( " ---------- ---------- ---------- " , color = C . COLOR_OK )
2015-12-09 16:51:12 +01:00
for secret in secrets :
display . display ( " %-10s %-10s %s / %s " % ( secret [ ' id ' ] , secret [ ' source ' ] , secret [ ' github_user ' ] ,
2015-12-29 21:41:00 +01:00
secret [ ' github_repo ' ] ) , color = C . COLOR_OK )
2015-12-09 16:51:12 +01:00
return 0
if self . options . remove_id :
# Remove a secret
self . api . remove_secret ( self . options . remove_id )
2015-12-29 21:41:00 +01:00
display . display ( " Secret removed. Integrations using this secret will not longer work. " , color = C . COLOR_OK )
2015-12-09 16:51:12 +01:00
return 0
if len ( self . args ) < 4 :
raise AnsibleError ( " Missing one or more arguments. Expecting: source github_user github_repo secret " )
return 0
2015-12-12 19:43:10 +01:00
2015-12-09 16:51:12 +01:00
secret = self . args . pop ( )
github_repo = self . args . pop ( )
github_user = self . args . pop ( )
source = self . args . pop ( )
resp = self . api . add_secret ( source , github_user , github_repo , secret )
display . display ( " Added integration for %s %s / %s " % ( resp [ ' source ' ] , resp [ ' github_user ' ] , resp [ ' github_repo ' ] ) )
return 0
def execute_delete ( self ) :
"""
Delete a role from galaxy . ansible . com
"""
if len ( self . args ) < 2 :
raise AnsibleError ( " Missing one or more arguments. Expected: github_user github_repo " )
2015-12-12 19:43:10 +01:00
2015-12-09 16:51:12 +01:00
github_repo = self . args . pop ( )
github_user = self . args . pop ( )
resp = self . api . delete_role ( github_user , github_repo )
if len ( resp [ ' deleted_roles ' ] ) > 1 :
display . display ( " Deleted the following roles: " )
display . display ( " ID User Name " )
display . display ( " ------ --------------- ---------- " )
for role in resp [ ' deleted_roles ' ] :
display . display ( " %-8s %-15s %s " % ( role . id , role . namespace , role . name ) )
2015-12-12 19:43:10 +01:00
2015-12-09 16:51:12 +01:00
display . display ( resp [ ' status ' ] )
return True