|
|
|
@ -19,23 +19,14 @@
|
|
|
|
|
#
|
|
|
|
|
########################################################################
|
|
|
|
|
|
|
|
|
|
import datetime
|
|
|
|
|
import json
|
|
|
|
|
import os
|
|
|
|
|
import os.path
|
|
|
|
|
import shutil
|
|
|
|
|
import subprocess
|
|
|
|
|
import sys
|
|
|
|
|
import tarfile
|
|
|
|
|
import tempfile
|
|
|
|
|
import urllib
|
|
|
|
|
import urllib2
|
|
|
|
|
import yaml
|
|
|
|
|
|
|
|
|
|
from collections import defaultdict
|
|
|
|
|
from distutils.version import LooseVersion
|
|
|
|
|
from jinja2 import Environment
|
|
|
|
|
from optparse import OptionParser
|
|
|
|
|
|
|
|
|
|
import ansible.constants as C
|
|
|
|
|
import ansible.utils
|
|
|
|
@ -46,12 +37,12 @@ from ansible.galaxy import Galaxy
|
|
|
|
|
from ansible.galaxy.api import GalaxyAPI
|
|
|
|
|
from ansible.galaxy.role import GalaxyRole
|
|
|
|
|
from ansible.playbook.role.requirement import RoleRequirement
|
|
|
|
|
from ansible.utils.display import Display
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class GalaxyCLI(CLI):
|
|
|
|
|
|
|
|
|
|
VALID_ACTIONS = ("init", "info", "install", "list", "remove")
|
|
|
|
|
SKIP_INFO_KEYS = ("platforms","readme_html", "related", "summary_fields", "average_aw_composite", "average_aw_score", "url" )
|
|
|
|
|
VALID_ACTIONS = ("init", "info", "install", "list", "remove", "search")
|
|
|
|
|
SKIP_INFO_KEYS = ("name", "description", "readme_html", "related", "summary_fields", "average_aw_composite", "average_aw_score", "url" )
|
|
|
|
|
|
|
|
|
|
def __init__(self, args, display=None):
|
|
|
|
|
|
|
|
|
@ -72,47 +63,46 @@ class GalaxyCLI(CLI):
|
|
|
|
|
|
|
|
|
|
# options specific to actions
|
|
|
|
|
if self.action == "info":
|
|
|
|
|
self.parser.set_usage("usage: %prog info [options] role_name[,version]")
|
|
|
|
|
self.parser.set_usage("usage: %prog info [options] role_name[,version]")
|
|
|
|
|
elif self.action == "init":
|
|
|
|
|
self.parser.set_usage("usage: %prog init [options] role_name")
|
|
|
|
|
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.')
|
|
|
|
|
self.parser.add_option(
|
|
|
|
|
self.parser.set_usage("usage: %prog init [options] role_name")
|
|
|
|
|
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.')
|
|
|
|
|
self.parser.add_option(
|
|
|
|
|
'--offline', dest='offline', default=False, action='store_true',
|
|
|
|
|
help="Don't query the galaxy API when creating roles")
|
|
|
|
|
elif self.action == "install":
|
|
|
|
|
self.parser.set_usage("usage: %prog install [options] [-r FILE | role_name(s)[,version] | scm+role_repo_url[,version] | tar_file(s)]")
|
|
|
|
|
self.parser.add_option(
|
|
|
|
|
'-i', '--ignore-errors', dest='ignore_errors', action='store_true', default=False,
|
|
|
|
|
self.parser.set_usage("usage: %prog install [options] [-r FILE | role_name(s)[,version] | scm+role_repo_url[,version] | tar_file(s)]")
|
|
|
|
|
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,
|
|
|
|
|
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',
|
|
|
|
|
self.parser.add_option('-r', '--role-file', dest='role_file',
|
|
|
|
|
help='A file containing a list of roles to be imported')
|
|
|
|
|
elif self.action == "remove":
|
|
|
|
|
self.parser.set_usage("usage: %prog remove role1 role2 ...")
|
|
|
|
|
self.parser.set_usage("usage: %prog remove role1 role2 ...")
|
|
|
|
|
elif self.action == "list":
|
|
|
|
|
self.parser.set_usage("usage: %prog list [role_name]")
|
|
|
|
|
self.parser.set_usage("usage: %prog list [role_name]")
|
|
|
|
|
elif self.action == "search":
|
|
|
|
|
self.parser.add_option('-P', '--platforms', dest='platforms',
|
|
|
|
|
help='list of OS platforms to filter by')
|
|
|
|
|
self.parser.add_option('-C', '--categories', dest='categories',
|
|
|
|
|
help='list of categories to filter by')
|
|
|
|
|
self.parser.set_usage("usage: %prog search [<search_term>] [-C <category1,category2>] [-P platform]")
|
|
|
|
|
|
|
|
|
|
# options that apply to more than one action
|
|
|
|
|
if self.action != "init":
|
|
|
|
|
self.parser.add_option(
|
|
|
|
|
'-p', '--roles-path', dest='roles_path', default=C.DEFAULT_ROLES_PATH,
|
|
|
|
|
self.parser.add_option('-p', '--roles-path', dest='roles_path', 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)')
|
|
|
|
|
|
|
|
|
|
if self.action in ("info","init","install"):
|
|
|
|
|
self.parser.add_option( '-s', '--server', dest='api_server', default="https://galaxy.ansible.com",
|
|
|
|
|
if self.action in ("info","init","install","search"):
|
|
|
|
|
self.parser.add_option('-s', '--server', dest='api_server', default="https://galaxy.ansible.com",
|
|
|
|
|
help='The API server destination')
|
|
|
|
|
|
|
|
|
|
if self.action in ("init","install"):
|
|
|
|
|
self.parser.add_option(
|
|
|
|
|
'-f', '--force', dest='force', action='store_true', default=False,
|
|
|
|
|
self.parser.add_option('-f', '--force', dest='force', action='store_true', default=False,
|
|
|
|
|
help='Force overwriting an existing role')
|
|
|
|
|
|
|
|
|
|
# get options, args and galaxy object
|
|
|
|
@ -127,7 +117,7 @@ class GalaxyCLI(CLI):
|
|
|
|
|
super(GalaxyCLI, self).run()
|
|
|
|
|
|
|
|
|
|
# if not offline, get connect to galaxy api
|
|
|
|
|
if self.action in ("info","install") or (self.action == 'init' and not self.options.offline):
|
|
|
|
|
if self.action in ("info","install", "search") or (self.action == 'init' and not self.options.offline):
|
|
|
|
|
api_server = self.options.api_server
|
|
|
|
|
self.api = GalaxyAPI(self.galaxy, api_server)
|
|
|
|
|
if not self.api:
|
|
|
|
@ -156,6 +146,65 @@ class GalaxyCLI(CLI):
|
|
|
|
|
if not self.get_opt("ignore_errors", False):
|
|
|
|
|
raise AnsibleError('- you can use --ignore-errors to skip failed roles and finish processing the list.')
|
|
|
|
|
|
|
|
|
|
def parse_requirements_files(self, role):
|
|
|
|
|
if 'role' in role:
|
|
|
|
|
# Old style: {role: "galaxy.role,version,name", other_vars: "here" }
|
|
|
|
|
role_info = role_spec_parse(role['role'])
|
|
|
|
|
if isinstance(role_info, dict):
|
|
|
|
|
# Warning: Slight change in behaviour here. name may be being
|
|
|
|
|
# overloaded. Previously, name was only a parameter to the role.
|
|
|
|
|
# Now it is both a parameter to the role and the name that
|
|
|
|
|
# ansible-galaxy will install under on the local system.
|
|
|
|
|
if 'name' in role and 'name' in role_info:
|
|
|
|
|
del role_info['name']
|
|
|
|
|
role.update(role_info)
|
|
|
|
|
else:
|
|
|
|
|
# New style: { src: 'galaxy.role,version,name', other_vars: "here" }
|
|
|
|
|
if 'github.com' in role["src"] and 'http' in role["src"] and '+' not in role["src"] and not role["src"].endswith('.tar.gz'):
|
|
|
|
|
role["src"] = "git+" + role["src"]
|
|
|
|
|
|
|
|
|
|
if '+' in role["src"]:
|
|
|
|
|
(scm, src) = role["src"].split('+')
|
|
|
|
|
role["scm"] = scm
|
|
|
|
|
role["src"] = src
|
|
|
|
|
|
|
|
|
|
if 'name' not in role:
|
|
|
|
|
role["name"] = GalaxyRole.url_to_spec(role["src"])
|
|
|
|
|
|
|
|
|
|
if 'version' not in role:
|
|
|
|
|
role['version'] = ''
|
|
|
|
|
|
|
|
|
|
if 'scm' not in role:
|
|
|
|
|
role['scm'] = None
|
|
|
|
|
|
|
|
|
|
return role
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _display_role_info(self, role_info):
|
|
|
|
|
|
|
|
|
|
text = "\nRole: %s \n" % role_info['name']
|
|
|
|
|
text += "\tdescription: %s \n" % role_info['description']
|
|
|
|
|
|
|
|
|
|
for k in sorted(role_info.keys()):
|
|
|
|
|
|
|
|
|
|
if k in self.SKIP_INFO_KEYS:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
if isinstance(role_info[k], dict):
|
|
|
|
|
text += "\t%s: \n" % (k)
|
|
|
|
|
for key in sorted(role_info[k].keys()):
|
|
|
|
|
if key in self.SKIP_INFO_KEYS:
|
|
|
|
|
continue
|
|
|
|
|
text += "\t\t%s: %s\n" % (key, role_info[k][key])
|
|
|
|
|
else:
|
|
|
|
|
text += "\t%s: %s\n" % (k, role_info[k])
|
|
|
|
|
|
|
|
|
|
return text
|
|
|
|
|
|
|
|
|
|
############################
|
|
|
|
|
# execute actions
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
def execute_init(self):
|
|
|
|
|
"""
|
|
|
|
|
Executes the init action, which creates the skeleton framework
|
|
|
|
@ -249,6 +298,7 @@ class GalaxyCLI(CLI):
|
|
|
|
|
|
|
|
|
|
roles_path = self.get_opt("roles_path")
|
|
|
|
|
|
|
|
|
|
data = ''
|
|
|
|
|
for role in self.args:
|
|
|
|
|
|
|
|
|
|
role_info = {}
|
|
|
|
@ -277,23 +327,12 @@ class GalaxyCLI(CLI):
|
|
|
|
|
if role_spec:
|
|
|
|
|
role_info.update(role_spec)
|
|
|
|
|
|
|
|
|
|
if role_info:
|
|
|
|
|
self.display.display("- %s:" % (role))
|
|
|
|
|
for k in sorted(role_info.keys()):
|
|
|
|
|
|
|
|
|
|
if k in self.SKIP_INFO_KEYS:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
if isinstance(role_info[k], dict):
|
|
|
|
|
self.display.display("\t%s: " % (k))
|
|
|
|
|
for key in sorted(role_info[k].keys()):
|
|
|
|
|
if key in self.SKIP_INFO_KEYS:
|
|
|
|
|
continue
|
|
|
|
|
self.display.display("\t\t%s: %s" % (key, role_info[k][key]))
|
|
|
|
|
else:
|
|
|
|
|
self.display.display("\t%s: %s" % (k, role_info[k]))
|
|
|
|
|
data += self._display_role_info(role_info)
|
|
|
|
|
if data:
|
|
|
|
|
data += "\n- %s:" % (role)
|
|
|
|
|
else:
|
|
|
|
|
self.display.display("- the role %s was not found" % role)
|
|
|
|
|
data += "\n- the role %s was not found" % role
|
|
|
|
|
self.pager(data)
|
|
|
|
|
|
|
|
|
|
def execute_install(self):
|
|
|
|
|
"""
|
|
|
|
@ -497,35 +536,22 @@ class GalaxyCLI(CLI):
|
|
|
|
|
self.display.display("- %s, %s" % (path_file, version))
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
def parse_requirements_files(self, role):
|
|
|
|
|
if 'role' in role:
|
|
|
|
|
# Old style: {role: "galaxy.role,version,name", other_vars: "here" }
|
|
|
|
|
role_info = role_spec_parse(role['role'])
|
|
|
|
|
if isinstance(role_info, dict):
|
|
|
|
|
# Warning: Slight change in behaviour here. name may be being
|
|
|
|
|
# overloaded. Previously, name was only a parameter to the role.
|
|
|
|
|
# Now it is both a parameter to the role and the name that
|
|
|
|
|
# ansible-galaxy will install under on the local system.
|
|
|
|
|
if 'name' in role and 'name' in role_info:
|
|
|
|
|
del role_info['name']
|
|
|
|
|
role.update(role_info)
|
|
|
|
|
else:
|
|
|
|
|
# New style: { src: 'galaxy.role,version,name', other_vars: "here" }
|
|
|
|
|
if 'github.com' in role["src"] and 'http' in role["src"] and '+' not in role["src"] and not role["src"].endswith('.tar.gz'):
|
|
|
|
|
role["src"] = "git+" + role["src"]
|
|
|
|
|
def execute_search(self):
|
|
|
|
|
|
|
|
|
|
if '+' in role["src"]:
|
|
|
|
|
(scm, src) = role["src"].split('+')
|
|
|
|
|
role["scm"] = scm
|
|
|
|
|
role["src"] = src
|
|
|
|
|
search = None
|
|
|
|
|
if len(self.args) > 1:
|
|
|
|
|
raise AnsibleOptionsError("At most a single search term is allowed.")
|
|
|
|
|
elif len(self.args) == 1:
|
|
|
|
|
search = self.args.pop()
|
|
|
|
|
|
|
|
|
|
if 'name' not in role:
|
|
|
|
|
role["name"] = GalaxyRole.url_to_spec(role["src"])
|
|
|
|
|
response = self.api.search_roles(search, self.options.platforms, self.options.categories)
|
|
|
|
|
|
|
|
|
|
if 'version' not in role:
|
|
|
|
|
role['version'] = ''
|
|
|
|
|
if 'count' in response:
|
|
|
|
|
self.galaxy.display.display("Found %d roles matching your search:\n" % response['count'])
|
|
|
|
|
|
|
|
|
|
if 'scm' not in role:
|
|
|
|
|
role['scm'] = None
|
|
|
|
|
data = ''
|
|
|
|
|
if 'results' in response:
|
|
|
|
|
for role in response['results']:
|
|
|
|
|
data += self._display_role_info(role)
|
|
|
|
|
|
|
|
|
|
return role
|
|
|
|
|
self.pager(data)
|
|
|
|
|