tripleo-common/scripts/create_freeipa_enroll_envfile.py
Juan Antonio Osorio Robles d7301268fa Add FreeIPA enrollment environment generator
This is based on previous work [1] and it's what I've been using to
test the TLS-everywhere work.

This is a convenience tool that will generate an environment file
that sets the override in the resource registry as well as the
relevant parameters that will be needed in a TLS-everywhere deployment.

[1] https://github.com/JAORMX/freeipa-tripleo-incubator

bp tls-via-certmonger

Change-Id: If3e6cb1dde235b24a0c188eb758f9a2e6a626f69
Depends-On: Iac94b3b047dca1bcabd464ea8eed6f1220c844f1
2016-12-13 14:36:53 +00:00

241 lines
8.9 KiB
Python
Executable File

#!/usr/bin/env python
# Copyright 2015 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Generate an environment file to enroll the controller nodes to FreeIPA via the
ExtraConfigPre hook.
Please note that this is only used for testing.
"""
from __future__ import print_function
import argparse
import collections
import logging
import os
from six.moves import input
import yaml
import tripleo_common.constants as tc_constants
logging.basicConfig(level=logging.INFO)
LOG = logging.getLogger(__name__)
autogen_warning = """### DO NOT MODIFY THIS FILE
### created by the script create_freeipa_enroll_envfile.py
"""
TEMPLATE_DEFAULT_PATH = os.path.join(
tc_constants.DEFAULT_TEMPLATES_PATH,
'puppet/extraconfig/tls/freeipa-enroll.yaml')
class TemplateDumper(yaml.SafeDumper):
def represent_ordered_dict(self, data):
return self.represent_dict(data.items())
TemplateDumper.add_representer(collections.OrderedDict,
TemplateDumper.represent_ordered_dict)
def write_env_file(output_file, env_dict):
with open(output_file, 'w') as f:
f.write(autogen_warning)
yaml.dump(env_dict, f, TemplateDumper, width=68,
default_flow_style=False)
LOG.info("environment file written to %s" % os.path.abspath(output_file))
def get_freeipa_parameter_defaults_dict(password, server, domain,
dns_servers, ipa_ip):
parameter_defaults = {
'FreeIPAOTP': password,
'FreeIPAServer': server,
'CloudDomain': domain,
}
if dns_servers:
parameter_defaults['DnsServers'] = dns_servers
if ipa_ip:
parameter_defaults['FreeIPAIPAddress'] = ipa_ip
return parameter_defaults
def get_freeipa_resource_registry_dict(stack):
resource_registry = {
'OS::TripleO::NodeTLSCAData': os.path.abspath(stack)
}
return resource_registry
def _form_fqdn(name, domain):
return "%s.%s" % (name, domain)
def get_cloud_names_parameter_defaults_dict(cloud_name,
cloud_name_internal,
cloud_name_storage,
cloud_name_storage_management,
cloud_name_ctlplane,
cloud_domain):
return {
'CloudName': _form_fqdn(cloud_name, cloud_domain),
'CloudNameInternal': _form_fqdn(cloud_name_internal, cloud_domain),
'CloudNameStorage': _form_fqdn(cloud_name_storage, cloud_domain),
'CloudNameStorageManagement': _form_fqdn(cloud_name_storage_management,
cloud_domain),
'CloudNameCtlplane': _form_fqdn(cloud_name_ctlplane, cloud_domain),
}
def get_freeipa_environment_dict(password, server, domain, dns_servers, ipa_ip,
stack, cloud_name, cloud_name_internal,
cloud_name_storage,
cloud_name_storage_management,
cloud_name_ctlplane):
enroll_dict = get_freeipa_parameter_defaults_dict(password, server, domain,
dns_servers, ipa_ip)
names_dict = get_cloud_names_parameter_defaults_dict(
cloud_name,
cloud_name_internal,
cloud_name_storage,
cloud_name_storage_management,
cloud_name_ctlplane,
domain)
enroll_dict.update(names_dict)
return get_environment_dict(enroll_dict,
get_freeipa_resource_registry_dict(stack))
def get_environment_dict(parameter_defaults=None, resource_registry=None):
resulting_dict = collections.OrderedDict()
if parameter_defaults:
resulting_dict['parameter_defaults'] = parameter_defaults
if resource_registry:
resulting_dict['resource_registry'] = resource_registry
return resulting_dict
def _confirm(message):
user_input = input(message)
return user_input.lower() in ['true', '1', 't', 'y', 'yes']
def _confirmation_if_output_file_exists(output, overwrite):
if os.path.isfile(output) and not overwrite:
LOG.warning("%s exists in the filesystem." % output)
if not _confirm("Do you want to overwrite it? "):
raise RuntimeError("%s exists and won't be overwritten." % output)
def _assert_stack_file_exists(stack):
if not os.path.isfile(stack):
raise IOError("%s file doesn't exist." % stack)
def _assert_not_empty(server, domain):
if not server or not domain:
raise RuntimeError(
"FreeIPA's server and the domain name can't be empty")
def _warn_unmatching_domain(server, domain):
if not server.endswith(domain):
LOG.warning(("FreeIPA's server domain doesn't seem to match the given "
"domain %s ... watch out") % domain)
def _validate_input(args):
_confirmation_if_output_file_exists(args.output, args.overwrite)
_assert_stack_file_exists(args.stack)
_assert_not_empty(args.server, args.domain)
_warn_unmatching_domain(args.server, args.domain)
def _get_options():
parser = argparse.ArgumentParser(description=__doc__)
# Base stack arguments
parser.add_argument('-w', '--password', required=True,
help='The OTP that will be used for the nodes.')
parser.add_argument('-s', '--server', required=True,
help="The FreeIPA server's fqdn.")
parser.add_argument('-d', '--domain', required=True,
help=("The FreeIPA managed domain (must match the "
"kerberos realm."))
parser.add_argument('-D', '--dns-server', action='append',
help=("The DNS server(s) that the overcloud should "
"have configured."))
parser.add_argument('-i', '--ipa-ip',
help="The FreeIPA server's IP address.")
parser.add_argument('-S', '--stack',
default=TEMPLATE_DEFAULT_PATH,
help=("stack template that will be used if not "
"default."))
parser.add_argument('-o', '--output',
default='freeipa-enroll.yaml',
help=('file that the freeipa-related environment will '
'be written to.'))
# Cloud name environment arguments
parser.add_argument('--cloud-name',
default='overcloud',
help=("The shortname for the overcloud (the domain "
"will be appended to this)."))
parser.add_argument('--cloud-name-internal',
default='overcloud.internalapi',
help=("The shortname name of the overcloud's internal "
"API endpoint (the domain will be appended to "
"this)."))
parser.add_argument('--cloud-name-storage',
default='overcloud.storage',
help=("The shortname name of the overcloud's storage "
"endpoint (the domain will be appended to "
"this)."))
parser.add_argument('--cloud-name-storage-management',
default='overcloud.storagemgmt',
help=("The shortname name of the overcloud's storage "
" management endpoint (the domain will be "
"appended to this)."))
parser.add_argument('--cloud-name-ctlplane',
default='overcloud.ctlplane',
help=("The shortname name of the overcloud's "
"ctlplane endpoint (the domain will be "
"appended to this)."))
# Extra
parser.add_argument('--overwrite', action='store_true',
help='overwrite the output file if it already exists.')
return parser.parse_args()
def main():
args = _get_options()
_validate_input(args)
enroll_dict = get_freeipa_environment_dict(
args.password, args.server, args.domain, args.dns_server, args.ipa_ip,
args.stack, args.cloud_name, args.cloud_name_internal,
args.cloud_name_storage, args.cloud_name_storage_management,
args.cloud_name_ctlplane)
write_env_file(args.output, enroll_dict)
if __name__ == '__main__':
main()