mirror of
https://github.com/ansible/awx.git
synced 2026-01-11 10:00:01 -03:30
Azure inventory sync!
This commit is contained in:
parent
504b810418
commit
cf96240556
@ -425,6 +425,11 @@ def load_inventory_source(source, all_group=None, group_filter_re=None,
|
||||
'''
|
||||
Load inventory from given source directory or file.
|
||||
'''
|
||||
# Sanity check: We need the "azure" module to be titled "windows_azure.py",
|
||||
# because it depends on the "azure" package from PyPI, and naming the
|
||||
# module the same way makes the importer sad.
|
||||
source = source.replace('azure', 'windows_azure')
|
||||
|
||||
logger.debug('Analyzing type of source: %s', source)
|
||||
original_all_group = all_group
|
||||
if not os.path.exists(source):
|
||||
|
||||
@ -201,15 +201,39 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique):
|
||||
return password
|
||||
|
||||
def _validate_ssh_private_key(self, data):
|
||||
"""Validate that the given SSH private key or certificate is,
|
||||
in fact, valid.
|
||||
"""
|
||||
cert = ''
|
||||
data = data.strip()
|
||||
validation_error = ValidationError('Invalid SSH private key')
|
||||
begin_re = re.compile(r'^(-{4,})\s*BEGIN\s+([A-Z0-9]+)?\s*PRIVATE\sKEY\s*(-{4,})$')
|
||||
header_re = re.compile(r'^(.+?):\s*?(.+?)(\\??)$')
|
||||
end_re = re.compile(r'^(-{4,})\s*END\s+([A-Z0-9]+)?\s*PRIVATE\sKEY\s*(-{4,})$')
|
||||
lines = data.strip().splitlines()
|
||||
|
||||
# Set up the valid private key header and footer.
|
||||
begin_re = r'^(-{4,})\s*BEGIN\s+([A-Z0-9]+)?\s*PRIVATE\sKEY\s*(-{4,})$'
|
||||
end_re = r'^(-{4,})\s*END\s+([A-Z0-9]+)?\s*PRIVATE\sKEY\s*(-{4,})$'
|
||||
|
||||
# Sanity check: We may potentially receive a full PEM certificate,
|
||||
# and we want to accept these.
|
||||
cert_re = r'^(-{4,})\s*BEGIN\s+CERTIFICATE\s*(-{4,})'
|
||||
cert_match = re.search(cert_re, data)
|
||||
if cert_match:
|
||||
private_key_begin = re.search(begin_re[1:-1], data)
|
||||
if not private_key_begin:
|
||||
raise validation_error
|
||||
boundary = private_key_begin.start()
|
||||
cert = data[:boundary].strip()
|
||||
data = data[boundary:].strip()
|
||||
|
||||
# Split the SSH key into individual lines.
|
||||
# If we have no content at all, then this is not a valid SSH key.
|
||||
lines = data.splitlines()
|
||||
if not lines:
|
||||
raise validation_error
|
||||
begin_match = begin_re.match(lines[0])
|
||||
end_match = end_re.match(lines[-1])
|
||||
|
||||
# Match the beginning and ending against what we expect, and also
|
||||
# ensure that they match one another.
|
||||
begin_match = re.match(begin_re, lines[0])
|
||||
end_match = re.match(end_re, lines[-1])
|
||||
if not begin_match or not end_match:
|
||||
raise validation_error
|
||||
dashes = set([begin_match.groups()[0], begin_match.groups()[2],
|
||||
@ -219,25 +243,40 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique):
|
||||
if begin_match.groups()[1] != end_match.groups()[1]:
|
||||
raise validation_error
|
||||
line_continues = False
|
||||
|
||||
# Establish that we are able to base64 decode the private key;
|
||||
# if we can't, then it's not a valid key.
|
||||
#
|
||||
# If we got a certificate, validate that also, in the same way.
|
||||
header_re = re.compile(r'^(.+?):\s*?(.+?)(\\??)$')
|
||||
base64_data = ''
|
||||
for line in lines[1:-1]:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
for segment_to_validate in (cert, data):
|
||||
# If we have nothing; skip this one.
|
||||
# We've already validated that we have a private key above,
|
||||
# so we don't need to do it again.
|
||||
if not segment_to_validate:
|
||||
continue
|
||||
if line_continues:
|
||||
line_continues = line.endswith('\\')
|
||||
continue
|
||||
line_match = header_re.match(line)
|
||||
if line_match:
|
||||
line_continues = line.endswith('\\')
|
||||
continue
|
||||
base64_data += line
|
||||
try:
|
||||
decoded_data = base64.b64decode(base64_data)
|
||||
if not decoded_data:
|
||||
|
||||
# Ensure that this segment is valid base64 data.
|
||||
lines = segment_to_validate.splitlines()
|
||||
for line in lines[1:-1]:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
if line_continues:
|
||||
line_continues = line.endswith('\\')
|
||||
continue
|
||||
line_match = header_re.match(line)
|
||||
if line_match:
|
||||
line_continues = line.endswith('\\')
|
||||
continue
|
||||
base64_data += line
|
||||
try:
|
||||
decoded_data = base64.b64decode(base64_data)
|
||||
if not decoded_data:
|
||||
raise validation_error
|
||||
except TypeError:
|
||||
raise validation_error
|
||||
except TypeError:
|
||||
raise validation_error
|
||||
|
||||
def clean_ssh_key_data(self):
|
||||
if self.pk:
|
||||
|
||||
@ -85,7 +85,9 @@ class AzureInventory(object):
|
||||
elif not self.is_cache_valid():
|
||||
self.do_api_calls_update_cache()
|
||||
|
||||
if self.args.list_images:
|
||||
if self.args.host:
|
||||
data_to_print = self.get_host(self.args.host)
|
||||
elif self.args.list_images:
|
||||
data_to_print = self.json_format_dict(self.get_images(), True)
|
||||
elif self.args.list:
|
||||
# Display list of nodes for inventory
|
||||
@ -96,6 +98,23 @@ class AzureInventory(object):
|
||||
|
||||
print data_to_print
|
||||
|
||||
def get_host(self, hostname):
|
||||
"""Return information about the given hostname, based on what
|
||||
the Windows Azure API provides.
|
||||
"""
|
||||
# Strip ".cloudapp.net" off of the end of the hostname if
|
||||
# it is present.
|
||||
if hostname.endswith('.cloudapp.net'):
|
||||
hostname = hostname.replace('.cloudapp.net', '')
|
||||
|
||||
# Retrieve information about the host.
|
||||
host = self.sms.get_hosted_service_properties(hostname)
|
||||
hsp = host.hosted_service_properties # Because reasons.
|
||||
return json.dumps({
|
||||
'label': hsp.label,
|
||||
'status': hsp.status.lower(),
|
||||
})
|
||||
|
||||
def get_images(self):
|
||||
images = []
|
||||
for image in self.sms.list_os_images():
|
||||
@ -142,13 +161,20 @@ class AzureInventory(object):
|
||||
|
||||
def parse_cli_args(self):
|
||||
"""Command line argument processing"""
|
||||
parser = argparse.ArgumentParser(description='Produce an Ansible Inventory file based on Azure')
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Produce an Ansible Inventory file based on Azure',
|
||||
)
|
||||
parser.add_argument('--list', action='store_true', default=True,
|
||||
help='List nodes (default: True)')
|
||||
help='List nodes (default: True)')
|
||||
parser.add_argument('--list-images', action='store',
|
||||
help='Get all available images.')
|
||||
parser.add_argument('--refresh-cache', action='store_true', default=False,
|
||||
help='Force refresh of cache by making API requests to Azure (default: False - use cache files)')
|
||||
help='Get all available images.')
|
||||
parser.add_argument('--refresh-cache',
|
||||
action='store_true', default=False,
|
||||
help='Force refresh of thecache by making API requests to Azure '
|
||||
'(default: False - use cache files)',
|
||||
)
|
||||
parser.add_argument('--host', action='store',
|
||||
help='Get all information about an instance.')
|
||||
self.args = parser.parse_args()
|
||||
|
||||
def do_api_calls_update_cache(self):
|
||||
|
||||
@ -443,7 +443,7 @@ GCE_INSTANCE_ID_VAR = None
|
||||
|
||||
# It's not possible to get zones in Azure without authenticating, so we
|
||||
# provide a list here.
|
||||
WA_REGION_CHOICES = [
|
||||
AZURE_REGION_CHOICES = [
|
||||
('Central_US', 'US Central'),
|
||||
('East_US_1', 'US East'),
|
||||
('East_US_2', 'US East 2'),
|
||||
@ -458,19 +458,19 @@ WA_REGION_CHOICES = [
|
||||
('West_Japan', 'Japan West'),
|
||||
('South_Brazil', 'Brazil South'),
|
||||
]
|
||||
WA_REGIONS_BLACKLIST = []
|
||||
AZURE_REGIONS_BLACKLIST = []
|
||||
|
||||
# Inventory variable name/value for determining whether a host is active
|
||||
# in Google Compute Engine.
|
||||
WA_ENABLED_VAR = 'status'
|
||||
WA_ENABLED_VALUE = 'running'
|
||||
# in Windows Azure.
|
||||
AZURE_ENABLED_VAR = 'status'
|
||||
AZURE_ENABLED_VALUE = 'created'
|
||||
|
||||
# Filter for allowed group and host names when importing inventory from
|
||||
# Google Compute Engine.
|
||||
WA_GROUP_FILTER = r'^.+$'
|
||||
WA_HOST_FILTER = r'^.+$'
|
||||
WA_EXCLUDE_EMPTY_GROUPS = True
|
||||
WA_INSTANCE_ID_VAR = None
|
||||
# Windows Azure.
|
||||
AZURE_GROUP_FILTER = r'^.+$'
|
||||
AZURE_HOST_FILTER = r'^.+$'
|
||||
AZURE_EXCLUDE_EMPTY_GROUPS = True
|
||||
AZURE_INSTANCE_ID_VAR = None
|
||||
|
||||
|
||||
# ---------------------
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user