-
-
Save dmuntean/f784398be33e5bc10aa43da35f05315b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python | |
| # 2018-02-15 Updated to be compatible with Salt-Cloud 2017.7.3 | |
| import sys | |
| import os.path | |
| import yaml | |
| import boto3 | |
| import salt.config | |
| import salt.wheel | |
| import salt.cloud | |
| from salt.exceptions import SaltInvocationError | |
| import tempfile | |
| from salt.utils.templates import render_jinja_tmpl | |
| def autosign(name, output=True): | |
| ''' | |
| Create a file in minions_autosign to pre-approve a minion | |
| ''' | |
| ret = {} | |
| autosign_key = os.path.join(__opts__['pki_dir'], 'minions_autosign', name) | |
| open(autosign_key, 'a').close() | |
| ret['key'] = autosign_key | |
| return ret | |
| def profile(profile_name, name): | |
| ''' | |
| Pass is a profile to create a new EC2 instance with the name provided. | |
| profile is the name of the Salt Cloud profile to use in creating the EC2 instance. | |
| name is the name of the instance | |
| ''' | |
| # | |
| # Read in the Salt Master and Salt Cloud configurations | |
| # | |
| ret = {} | |
| opts = salt.config.cloud_config('/etc/salt/cloud') | |
| cloud = salt.cloud.CloudClient('/etc/salt/cloud') | |
| wheel = salt.wheel.WheelClient(opts) | |
| try: | |
| profile = opts['profiles'][profile_name] | |
| except KeyError: | |
| raise SaltInvocationError('Profile %s not found' % profile) | |
| provider_name, provider_type = profile.get('provider').split(':') | |
| try: | |
| provider = opts['providers'][provider_name][provider_type] | |
| except KeyError: | |
| raise SaltInvocationError('Provider %s not found' % profile['provider']) | |
| minion_id = name | |
| # This dict should match what Cloud passes around internally | |
| vm_ = { | |
| 'provider': provider_name, | |
| 'profile': profile_name, | |
| 'driver': provider_name, | |
| 'name': name | |
| } | |
| # Deploy must be set to False to prevent conflict | |
| if salt.config.get_cloud_config_value('deploy', vm_, opts) == True: | |
| raise SaltCloudConfigError('Deploy can not be true') | |
| # Empty dict to hold configuration for create_instances | |
| cfg = {} | |
| instance_cfg = {} | |
| # userdata_file | |
| cfg['userdata_file'] = salt.config.get_cloud_config_value('userdata_file', vm_, opts) | |
| if cfg['userdata_file']: | |
| profile_fields = [ | |
| 'provider', | |
| 'image', | |
| 'size', | |
| 'ssh_username', | |
| 'volumes', | |
| 'tag', | |
| 'sync_after_install', | |
| 'keyname', | |
| 'securitygroup', | |
| 'iam_profile', | |
| 'ssh_interface', | |
| 'ssh_gateway', | |
| 'ssh_gateway_port', | |
| 'ssh_gateway_username', | |
| 'ssh_gateway_private_key', | |
| 'ssh_gateway_password', | |
| 'location', | |
| 'availability_zone', | |
| 'tenancy', | |
| 'subnetid', | |
| 'subnetname', | |
| 'securitygroupid', | |
| 'securitygroupname', | |
| 'placementgroup', | |
| 'spot_config', | |
| 'block_device_mappings', | |
| 'min_instance', | |
| 'max_instance', | |
| 'userdata_file', | |
| 'userdata', | |
| 'network_interfaces', | |
| 'ebs_optimized', | |
| 'del_root_vol_on_destroy', | |
| ] | |
| # Create a temporary file to hold the result of the userdata_file template render | |
| userdata_tmp = tempfile.NamedTemporaryFile(delete=False) | |
| # image | |
| cfg['image'] = salt.config.get_cloud_config_value('image', vm_, opts) | |
| if cfg['image']: | |
| instance_cfg['ImageId'] = cfg['image'] | |
| # keyname | |
| cfg['keyname'] = salt.config.get_cloud_config_value('keyname', vm_, opts) | |
| if cfg['keyname']: | |
| instance_cfg['KeyName'] = cfg['keyname'] | |
| # size | |
| cfg['size'] = salt.config.get_cloud_config_value('size', vm_, opts) | |
| if cfg['size']: | |
| instance_cfg['InstanceType'] = cfg['size'] | |
| # minion | |
| cfg['minion'] = salt.config.get_cloud_config_value('minion', vm_, opts) | |
| # iam_profile | |
| cfg['iam_profile'] = salt.config.get_cloud_config_value('iam_profile', vm_, opts) | |
| if cfg['iam_profile']: | |
| instance_cfg['IamInstanceProfile'] = {'Arn': cfg['iam_profile']} | |
| # Render the cloud-init template via Jinja | |
| init_cxt = { | |
| 'opts': opts, | |
| 'saltenv': None, | |
| 'minion_id': minion_id, | |
| 'profile': cfg | |
| } | |
| # Read in the cloud-init template | |
| init_tpl = open(cfg['userdata_file']).read() | |
| # Render the template | |
| init_rdr = render_jinja_tmpl(init_tpl, init_cxt) | |
| instance_cfg['UserData'] = init_rdr | |
| # Save the result to a temporary file | |
| userdata_tmp.write(init_rdr + '\n') | |
| userdata_tmp.close() | |
| # | |
| # If we're given a minion ID (to manually create an instance) then | |
| # generate the minion key and user data now | |
| # | |
| aws_access_id = salt.config.get_cloud_config_value('id', vm_, opts) | |
| aws_secret_key = salt.config.get_cloud_config_value('key', vm_, opts) | |
| if not (aws_access_id and aws_secret_key): | |
| raise SaltInvocationError('Need S3 credentials') | |
| s3_bucket = salt.config.get_cloud_config_value('s3_bucket', vm_, opts) | |
| s3_path = salt.config.get_cloud_config_value('s3_path', vm_, opts) | |
| if s3_bucket and s3_path: | |
| keys = wheel.cmd('key.gen_accept', [minion_id]) | |
| # If the key has already been accepted `keys` will be an empty dict | |
| if keys == {}: | |
| print('Minion key for %s already exists. No key will be created' % minion_id) | |
| else: | |
| # Upload the new keys to S3 | |
| s3 = boto3.client('s3', aws_access_key_id=aws_access_id, | |
| aws_secret_access_key=aws_secret_key) | |
| s3.put_object(Key='%s/%s.pem' % (s3_path, minion_id), Bucket=s3_bucket, Body=keys['priv']) | |
| s3.put_object(Key='%s/%s.pub' % (s3_path, minion_id), Bucket=s3_bucket, Body=keys['pub']) | |
| # Create a new instance in EC2 | |
| vm_overrides = {} | |
| if 'userdata_file' in cfg and cfg['userdata_file']: | |
| # Override the userdata_file parameter to point to our rendered temporary file | |
| vm_overrides['userdata_file'] = userdata_tmp.name | |
| instance = cloud.profile(profile_name, [minion_id,], vm_overrides=vm_overrides) | |
| ret['instance'] = instance | |
| ret['keys'] = { | |
| 'priv': 's3://%s/%s/%s.pem' % (s3_bucket, s3_path, minion_id), | |
| 'pub': 's3://%s/%s/%s.pub' % (s3_bucket, s3_path, minion_id), | |
| } | |
| return ret | |
| else: | |
| raise SaltInvocationError('No S3 bucket and path provided.') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment