Mini Shell
"""
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 logging
import os
import tempfile
import salt.client
import salt.config as config
import salt.utils.cloud
from salt._compat import ipaddress
from salt.exceptions import SaltCloudException, SaltCloudSystemExit, SaltInvocationError
log = logging.getLogger(__name__)
def __virtual__():
"""
Needs no special configuration
"""
return True
def _get_active_provider_name():
try:
return __active_provider_name__.value()
except AttributeError:
return __active_provider_name__
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.
"""
with salt.client.LocalClient() as local:
return local.cmd(
"salt-cloud:driver:vagrant", "grains.items", "", tgt_type="grain"
)
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.
"""
with salt.client.LocalClient() as local:
ret = local.cmd(name, "grains.items", "")
reqs = _build_required_items(ret)
ret[name].update(reqs[name])
return ret
def _get_my_info(name):
with salt.client.LocalClient() as local:
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)
with salt.client.LocalClient() as local:
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(
f"Insufficient SSH addressing information for {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__, _get_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",
f"salt/cloud/{name}/destroying",
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"]
with salt.client.LocalClient() as local:
ret = local.cmd(host, "vagrant.destroy", [name])
if ret[host]:
__utils__["cloud.fire_event"](
"event",
"destroyed instance",
f"salt/cloud/{name}/destroyed",
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, _get_active_provider_name().split(":")[0], opts
)
return {"Destroyed": f"{name} was destroyed."}
else:
return {"Error": f"Error destroying {name}"}
else:
return {"Error": f"No response from {name}. Cannot destroy."}
# 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"]
with salt.client.LocalClient() as local:
return local.cmd(host, "vagrant.reboot", [name])
Zerion Mini Shell 1.0