File: //usr/lib/python2.7/site-packages/salt/cloud/clouds/vagrant.py
# -*- coding: utf-8 -*-
'''
Vagrant Cloud Driver
====================
The Vagrant cloud is designed to "vagrant up" a virtual machine as a
Salt minion.
Use of this module requires some configuration in cloud profile and provider
files as described in the
:ref:`Getting Started with Vagrant <getting-started-with-vagrant>` documentation.
.. versionadded:: 2018.3.0
'''
# Import python libs
from __future__ import absolute_import, print_function, unicode_literals
import logging
import os
import tempfile
# Import salt libs
import salt.utils.cloud
import salt.config as config
import salt.client
from salt._compat import ipaddress
from salt.exceptions import SaltCloudException, SaltCloudSystemExit, SaltInvocationError
# Get logging started
log = logging.getLogger(__name__)
def __virtual__():
'''
Needs no special configuration
'''
return True
def avail_locations(call=None):
r'''
This function returns a list of locations available.
CLI Example:
.. code-block:: bash
salt-cloud --list-locations my-cloud-provider
# \[ vagrant will always returns an empty dictionary \]
'''
return {}
def avail_images(call=None):
'''This function returns a list of images available for this cloud provider.
vagrant will return a list of profiles.
salt-cloud --list-images my-cloud-provider
'''
vm_ = get_configured_provider()
return {'Profiles': [profile for profile in vm_['profiles']]}
def avail_sizes(call=None):
r'''
This function returns a list of sizes available for this cloud provider.
CLI Example:
.. code-block:: bash
salt-cloud --list-sizes my-cloud-provider
# \[ vagrant always returns an empty dictionary \]
'''
return {}
def list_nodes(call=None):
'''
List the nodes which have salt-cloud:driver:vagrant grains.
CLI Example:
.. code-block:: bash
salt-cloud -Q
'''
nodes = _list_nodes(call)
return _build_required_items(nodes)
def _build_required_items(nodes):
ret = {}
for name, grains in nodes.items():
if grains:
private_ips = []
public_ips = []
ips = grains['ipv4'] + grains['ipv6']
for adrs in ips:
ip_ = ipaddress.ip_address(adrs)
if not ip_.is_loopback:
if ip_.is_private:
private_ips.append(adrs)
else:
public_ips.append(adrs)
ret[name] = {
'id': grains['id'],
'image': grains['salt-cloud']['profile'],
'private_ips': private_ips,
'public_ips': public_ips,
'size': '',
'state': 'running'
}
return ret
def list_nodes_full(call=None):
'''
List the nodes, ask all 'vagrant' minions, return dict of grains (enhanced).
CLI Example:
.. code-block:: bash
salt-call -F
'''
ret = _list_nodes(call)
for key, grains in ret.items(): # clean up some hyperverbose grains -- everything is too much
try:
del grains['cpu_flags'], grains['disks'], grains['pythonpath'], grains['dns'], grains['gpus']
except KeyError:
pass # ignore absence of things we are eliminating
except TypeError:
del ret[key] # eliminate all reference to unexpected (None) values.
reqs = _build_required_items(ret)
for name in ret:
ret[name].update(reqs[name])
return ret
def _list_nodes(call=None):
'''
List the nodes, ask all 'vagrant' minions, return dict of grains.
'''
local = salt.client.LocalClient()
ret = local.cmd('salt-cloud:driver:vagrant', 'grains.items', '', tgt_type='grain')
return ret
def list_nodes_select(call=None):
'''
Return a list of the minions that have salt-cloud grains, with
select fields.
'''
return salt.utils.cloud.list_nodes_select(
list_nodes_full('function'), __opts__['query.selection'], call,
)
def show_instance(name, call=None):
'''
List the a single node, return dict of grains.
'''
local = salt.client.LocalClient()
ret = local.cmd(name, 'grains.items', '')
reqs = _build_required_items(ret)
ret[name].update(reqs[name])
return ret
def _get_my_info(name):
local = salt.client.LocalClient()
return local.cmd(name, 'grains.get', ['salt-cloud'])
def create(vm_):
'''
Provision a single machine
CLI Example:
.. code-block:: bash
salt-cloud -p my_profile new_node_1
'''
name = vm_['name']
machine = config.get_cloud_config_value(
'machine', vm_, __opts__, default='')
vm_['machine'] = machine
host = config.get_cloud_config_value(
'host', vm_, __opts__, default=NotImplemented)
vm_['cwd'] = config.get_cloud_config_value(
'cwd', vm_, __opts__, default='/')
vm_['runas'] = config.get_cloud_config_value(
'vagrant_runas', vm_, __opts__, default=os.getenv('SUDO_USER'))
vm_['timeout'] = config.get_cloud_config_value(
'vagrant_up_timeout', vm_, __opts__, default=300)
vm_['vagrant_provider'] = config.get_cloud_config_value(
'vagrant_provider', vm_, __opts__, default='')
vm_['grains'] = {'salt-cloud:vagrant': {'host': host, 'machine': machine}}
log.info('sending \'vagrant.init %s machine=%s\' command to %s', name, machine, host)
local = salt.client.LocalClient()
ret = local.cmd(host, 'vagrant.init', [name], kwarg={'vm': vm_, 'start': True})
log.info('response ==> %s', ret[host])
network_mask = config.get_cloud_config_value(
'network_mask', vm_, __opts__, default='')
if 'ssh_host' not in vm_:
ret = local.cmd(host,
'vagrant.get_ssh_config',
[name],
kwarg={'network_mask': network_mask,
'get_private_key': True})[host]
with tempfile.NamedTemporaryFile() as pks:
if 'private_key' not in vm_ and ret and ret.get('private_key', False):
pks.write(ret['private_key'])
pks.flush()
log.debug('wrote private key to %s', pks.name)
vm_['key_filename'] = pks.name
if 'ssh_host' not in vm_:
try:
vm_.setdefault('ssh_username', ret['ssh_username'])
if ret.get('ip_address'):
vm_['ssh_host'] = ret['ip_address']
else: # if probe failed or not used, use Vagrant's reported ssh info
vm_['ssh_host'] = ret['ssh_host']
vm_.setdefault('ssh_port', ret['ssh_port'])
except (KeyError, TypeError):
raise SaltInvocationError(
'Insufficient SSH addressing information for {}'.format(name))
log.info('Provisioning machine %s as node %s using ssh %s',
machine, name, vm_['ssh_host'])
ret = __utils__['cloud.bootstrap'](vm_, __opts__)
return ret
def get_configured_provider():
'''
Return the first configured instance.
'''
ret = config.is_provider_configured(
__opts__,
__active_provider_name__ or 'vagrant',
''
)
return ret
# noinspection PyTypeChecker
def destroy(name, call=None):
'''
Destroy a node.
CLI Example:
.. code-block:: bash
salt-cloud --destroy mymachine
'''
if call == 'function':
raise SaltCloudSystemExit(
'The destroy action must be called with -d, --destroy, '
'-a, or --action.'
)
opts = __opts__
__utils__['cloud.fire_event'](
'event',
'destroying instance',
'salt/cloud/{0}/destroying'.format(name),
args={'name': name},
sock_dir=opts['sock_dir'],
transport=opts['transport']
)
my_info = _get_my_info(name)
if my_info:
profile_name = my_info[name]['profile']
profile = opts['profiles'][profile_name]
host = profile['host']
local = salt.client.LocalClient()
ret = local.cmd(host, 'vagrant.destroy', [name])
if ret[host]:
__utils__['cloud.fire_event'](
'event',
'destroyed instance',
'salt/cloud/{0}/destroyed'.format(name),
args={'name': name},
sock_dir=opts['sock_dir'],
transport=opts['transport']
)
if opts.get('update_cachedir', False) is True:
__utils__['cloud.delete_minion_cachedir'](
name, __active_provider_name__.split(':')[0], opts)
return {'Destroyed': '{0} was destroyed.'.format(name)}
else:
return {'Error': 'Error destroying {}'.format(name)}
else:
return {'Error': 'No response from {}. Cannot destroy.'.format(name)}
# noinspection PyTypeChecker
def reboot(name, call=None):
'''
Reboot a vagrant minion.
name
The name of the VM to reboot.
CLI Example:
.. code-block:: bash
salt-cloud -a reboot vm_name
'''
if call != 'action':
raise SaltCloudException(
'The reboot action must be called with -a or --action.'
)
my_info = _get_my_info(name)
profile_name = my_info[name]['profile']
profile = __opts__['profiles'][profile_name]
host = profile['host']
local = salt.client.LocalClient()
return local.cmd(host, 'vagrant.reboot', [name])