mirror of
https://github.com/saltstack/salt.git
synced 2025-04-10 23:01:39 +00:00
901 lines
24 KiB
Python
901 lines
24 KiB
Python
"""
|
|
1&1 Cloud Server Module
|
|
=======================
|
|
|
|
The 1&1 SaltStack cloud module allows a 1&1 server to be automatically deployed
|
|
and bootstrapped with Salt. It also has functions to create block storages and
|
|
ssh keys.
|
|
|
|
:depends: 1and1 >= 1.2.0
|
|
|
|
The module requires the 1&1 api_token to be provided. The server should also
|
|
be assigned a public LAN, a private LAN, or both along with SSH key pairs.
|
|
|
|
Set up the cloud configuration at ``/etc/salt/cloud.providers`` or
|
|
``/etc/salt/cloud.providers.d/oneandone.conf``:
|
|
|
|
.. code-block:: yaml
|
|
|
|
my-oneandone-config:
|
|
driver: oneandone
|
|
# The 1&1 api token
|
|
api_token: <your-token>
|
|
# SSH private key filename
|
|
ssh_private_key: /path/to/private_key
|
|
# SSH public key filename
|
|
ssh_public_key: /path/to/public_key
|
|
|
|
.. code-block:: yaml
|
|
|
|
my-oneandone-profile:
|
|
provider: my-oneandone-config
|
|
# Either provide fixed_instance_size_id or vcore, cores_per_processor, ram, and hdds.
|
|
# Size of the ID desired for the server
|
|
fixed_instance_size: S
|
|
# Total amount of processors
|
|
vcore: 2
|
|
# Number of cores per processor
|
|
cores_per_processor: 2
|
|
# RAM memory size in GB
|
|
ram: 4
|
|
# Hard disks
|
|
hdds:
|
|
-
|
|
is_main: true
|
|
size: 20
|
|
-
|
|
is_main: false
|
|
size: 20
|
|
# ID of the appliance image that will be installed on server
|
|
appliance_id: <ID>
|
|
# ID of the datacenter where the server will be created
|
|
datacenter_id: <ID>
|
|
# Description of the server
|
|
description: My server description
|
|
# Password of the server. Password must contain more than 8 characters
|
|
# using uppercase letters, numbers and other special symbols.
|
|
password: P4$$w0rD
|
|
# Power on server after creation - default True
|
|
power_on: true
|
|
# Firewall policy ID. If it is not provided, the server will assign
|
|
# the best firewall policy, creating a new one if necessary.
|
|
# If the parameter is sent with a 0 value, the server will be created with all ports blocked.
|
|
firewall_policy_id: <ID>
|
|
# IP address ID
|
|
ip_id: <ID>
|
|
# Load balancer ID
|
|
load_balancer_id: <ID>
|
|
# Monitoring policy ID
|
|
monitoring_policy_id: <ID>
|
|
|
|
Set ``deploy`` to False if Salt should not be installed on the node.
|
|
|
|
.. code-block:: yaml
|
|
|
|
my-oneandone-profile:
|
|
deploy: False
|
|
|
|
Create an SSH key
|
|
|
|
.. code-block:: bash
|
|
|
|
sudo salt-cloud -f create_ssh_key my-oneandone-config name='SaltTest' description='SaltTestDescription'
|
|
|
|
Create a block storage
|
|
|
|
.. code-block:: bash
|
|
|
|
sudo salt-cloud -f create_block_storage my-oneandone-config name='SaltTest2'
|
|
description='SaltTestDescription' size=50 datacenter_id='5091F6D8CBFEF9C26ACE957C652D5D49'
|
|
|
|
"""
|
|
|
|
import logging
|
|
import os
|
|
import pprint
|
|
import time
|
|
|
|
import salt.config as config
|
|
import salt.utils.cloud
|
|
import salt.utils.files
|
|
import salt.utils.stringutils
|
|
from salt.exceptions import (
|
|
SaltCloudConfigError,
|
|
SaltCloudExecutionFailure,
|
|
SaltCloudExecutionTimeout,
|
|
SaltCloudNotFound,
|
|
SaltCloudSystemExit,
|
|
)
|
|
|
|
try:
|
|
# pylint: disable=no-name-in-module
|
|
from oneandone.client import BlockStorage, Hdd, OneAndOneService, Server, SshKey
|
|
|
|
# pylint: enable=no-name-in-module
|
|
|
|
HAS_ONEANDONE = True
|
|
except ImportError:
|
|
HAS_ONEANDONE = False
|
|
|
|
# Get logging started
|
|
log = logging.getLogger(__name__)
|
|
|
|
__virtualname__ = "oneandone"
|
|
|
|
|
|
# Only load in this module if the 1&1 configurations are in place
|
|
def __virtual__():
|
|
"""
|
|
Check for 1&1 configurations.
|
|
"""
|
|
if get_configured_provider() is False:
|
|
return False
|
|
|
|
if get_dependencies() is False:
|
|
return False
|
|
|
|
return __virtualname__
|
|
|
|
|
|
def _get_active_provider_name():
|
|
try:
|
|
return __active_provider_name__.value()
|
|
except AttributeError:
|
|
return __active_provider_name__
|
|
|
|
|
|
def get_configured_provider():
|
|
"""
|
|
Return the first configured instance.
|
|
"""
|
|
return config.is_provider_configured(
|
|
__opts__, _get_active_provider_name() or __virtualname__, ("api_token",)
|
|
)
|
|
|
|
|
|
def get_dependencies():
|
|
"""
|
|
Warn if dependencies are not met.
|
|
"""
|
|
return config.check_driver_dependencies(
|
|
__virtualname__, {"oneandone": HAS_ONEANDONE}
|
|
)
|
|
|
|
|
|
def get_conn():
|
|
"""
|
|
Return a conn object for the passed VM data
|
|
"""
|
|
return OneAndOneService(
|
|
api_token=config.get_cloud_config_value(
|
|
"api_token", get_configured_provider(), __opts__, search_global=False
|
|
)
|
|
)
|
|
|
|
|
|
def get_size(vm_):
|
|
"""
|
|
Return the VM's size object
|
|
"""
|
|
vm_size = config.get_cloud_config_value(
|
|
"fixed_instance_size", vm_, __opts__, default=None, search_global=False
|
|
)
|
|
sizes = avail_sizes()
|
|
|
|
if not vm_size:
|
|
size = next((item for item in sizes if item["name"] == "S"), None)
|
|
return size
|
|
|
|
size = next(
|
|
(item for item in sizes if item["name"] == vm_size or item["id"] == vm_size),
|
|
None,
|
|
)
|
|
if size:
|
|
return size
|
|
|
|
raise SaltCloudNotFound(f"The specified size, '{vm_size}', could not be found.")
|
|
|
|
|
|
def get_image(vm_):
|
|
"""
|
|
Return the image object to use
|
|
"""
|
|
vm_image = config.get_cloud_config_value("image", vm_, __opts__).encode(
|
|
"ascii", "salt-cloud-force-ascii"
|
|
)
|
|
|
|
images = avail_images()
|
|
for key, value in images.items():
|
|
if vm_image and vm_image in (images[key]["id"], images[key]["name"]):
|
|
return images[key]
|
|
|
|
raise SaltCloudNotFound(f"The specified image, '{vm_image}', could not be found.")
|
|
|
|
|
|
def avail_locations(conn=None, call=None):
|
|
"""
|
|
List available locations/datacenters for 1&1
|
|
"""
|
|
if call == "action":
|
|
raise SaltCloudSystemExit(
|
|
"The avail_locations function must be called with "
|
|
"-f or --function, or with the --list-locations option"
|
|
)
|
|
|
|
datacenters = []
|
|
|
|
if not conn:
|
|
conn = get_conn()
|
|
|
|
for datacenter in conn.list_datacenters():
|
|
datacenters.append({datacenter["country_code"]: datacenter})
|
|
|
|
return {"Locations": datacenters}
|
|
|
|
|
|
def create_block_storage(kwargs=None, call=None):
|
|
"""
|
|
Create a block storage
|
|
"""
|
|
if call == "action":
|
|
raise SaltCloudSystemExit(
|
|
"The avail_locations function must be called with "
|
|
"-f or --function, or with the --list-locations option"
|
|
)
|
|
|
|
conn = get_conn()
|
|
|
|
# Assemble the composite block storage object.
|
|
block_storage = _get_block_storage(kwargs)
|
|
|
|
data = conn.create_block_storage(block_storage=block_storage)
|
|
|
|
return {"BlockStorage": data}
|
|
|
|
|
|
def _get_block_storage(kwargs):
|
|
"""
|
|
Construct a block storage instance from passed arguments
|
|
"""
|
|
if kwargs is None:
|
|
kwargs = {}
|
|
|
|
block_storage_name = kwargs.get("name", None)
|
|
block_storage_size = kwargs.get("size", None)
|
|
block_storage_description = kwargs.get("description", None)
|
|
datacenter_id = kwargs.get("datacenter_id", None)
|
|
server_id = kwargs.get("server_id", None)
|
|
|
|
block_storage = BlockStorage(name=block_storage_name, size=block_storage_size)
|
|
|
|
if block_storage_description:
|
|
block_storage.description = block_storage_description
|
|
|
|
if datacenter_id:
|
|
block_storage.datacenter_id = datacenter_id
|
|
|
|
if server_id:
|
|
block_storage.server_id = server_id
|
|
|
|
return block_storage
|
|
|
|
|
|
def _get_ssh_key(kwargs):
|
|
"""
|
|
Construct an SshKey instance from passed arguments
|
|
"""
|
|
ssh_key_name = kwargs.get("name", None)
|
|
ssh_key_description = kwargs.get("description", None)
|
|
public_key = kwargs.get("public_key", None)
|
|
|
|
return SshKey(
|
|
name=ssh_key_name, description=ssh_key_description, public_key=public_key
|
|
)
|
|
|
|
|
|
def create_ssh_key(kwargs=None, call=None):
|
|
"""
|
|
Create an ssh key
|
|
"""
|
|
if call == "action":
|
|
raise SaltCloudSystemExit(
|
|
"The avail_locations function must be called with "
|
|
"-f or --function, or with the --list-locations option"
|
|
)
|
|
|
|
conn = get_conn()
|
|
|
|
# Assemble the composite SshKey object.
|
|
ssh_key = _get_ssh_key(kwargs)
|
|
|
|
data = conn.create_ssh_key(ssh_key=ssh_key)
|
|
|
|
return {"SshKey": data}
|
|
|
|
|
|
def avail_images(conn=None, call=None):
|
|
"""
|
|
Return a list of the server appliances that are on the provider
|
|
"""
|
|
if call == "action":
|
|
raise SaltCloudSystemExit(
|
|
"The avail_images function must be called with "
|
|
"-f or --function, or with the --list-images option"
|
|
)
|
|
|
|
if not conn:
|
|
conn = get_conn()
|
|
|
|
ret = {}
|
|
|
|
for appliance in conn.list_appliances():
|
|
ret[appliance["name"]] = appliance
|
|
|
|
return ret
|
|
|
|
|
|
def avail_sizes(call=None):
|
|
"""
|
|
Return a dict of all available VM sizes on the cloud provider with
|
|
relevant data.
|
|
"""
|
|
if call == "action":
|
|
raise SaltCloudSystemExit(
|
|
"The avail_sizes function must be called with "
|
|
"-f or --function, or with the --list-sizes option"
|
|
)
|
|
|
|
conn = get_conn()
|
|
|
|
sizes = conn.fixed_server_flavors()
|
|
|
|
return sizes
|
|
|
|
|
|
def script(vm_):
|
|
"""
|
|
Return the script deployment object
|
|
"""
|
|
return salt.utils.cloud.os_script(
|
|
config.get_cloud_config_value("script", vm_, __opts__),
|
|
vm_,
|
|
__opts__,
|
|
salt.utils.cloud.salt_config_to_yaml(
|
|
salt.utils.cloud.minion_config(__opts__, vm_)
|
|
),
|
|
)
|
|
|
|
|
|
def list_nodes(conn=None, call=None):
|
|
"""
|
|
Return a list of VMs that are on the provider
|
|
"""
|
|
if call == "action":
|
|
raise SaltCloudSystemExit(
|
|
"The list_nodes function must be called with -f or --function."
|
|
)
|
|
|
|
if not conn:
|
|
conn = get_conn()
|
|
|
|
ret = {}
|
|
nodes = conn.list_servers()
|
|
|
|
for node in nodes:
|
|
public_ips = []
|
|
private_ips = []
|
|
ret = {}
|
|
|
|
size = node.get("hardware").get("fixed_instance_size_id", "Custom size")
|
|
|
|
if node.get("private_networks"):
|
|
for private_ip in node["private_networks"]:
|
|
private_ips.append(private_ip)
|
|
|
|
if node.get("ips"):
|
|
for public_ip in node["ips"]:
|
|
public_ips.append(public_ip["ip"])
|
|
|
|
server = {
|
|
"id": node["id"],
|
|
"image": node["image"]["id"],
|
|
"size": size,
|
|
"state": node["status"]["state"],
|
|
"private_ips": private_ips,
|
|
"public_ips": public_ips,
|
|
}
|
|
ret[node["name"]] = server
|
|
|
|
return ret
|
|
|
|
|
|
def list_nodes_full(conn=None, call=None):
|
|
"""
|
|
Return a list of the VMs that are on the provider, with all fields
|
|
"""
|
|
if call == "action":
|
|
raise SaltCloudSystemExit(
|
|
"The list_nodes_full function must be called with -f or --function."
|
|
)
|
|
|
|
if not conn:
|
|
conn = get_conn()
|
|
|
|
ret = {}
|
|
nodes = conn.list_servers()
|
|
|
|
for node in nodes:
|
|
ret[node["name"]] = node
|
|
|
|
return ret
|
|
|
|
|
|
def list_nodes_select(conn=None, call=None):
|
|
"""
|
|
Return a list of the VMs that are on the provider, with select fields
|
|
"""
|
|
if not conn:
|
|
conn = get_conn()
|
|
|
|
return salt.utils.cloud.list_nodes_select(
|
|
list_nodes_full(conn, "function"),
|
|
__opts__["query.selection"],
|
|
call,
|
|
)
|
|
|
|
|
|
def show_instance(name, call=None):
|
|
"""
|
|
Show the details from the provider concerning an instance
|
|
"""
|
|
if call != "action":
|
|
raise SaltCloudSystemExit(
|
|
"The show_instance action must be called with -a or --action."
|
|
)
|
|
|
|
nodes = list_nodes_full()
|
|
__utils__["cloud.cache_node"](nodes[name], _get_active_provider_name(), __opts__)
|
|
return nodes[name]
|
|
|
|
|
|
def _get_server(vm_):
|
|
"""
|
|
Construct server instance from cloud profile config
|
|
"""
|
|
description = config.get_cloud_config_value(
|
|
"description", vm_, __opts__, default=None, search_global=False
|
|
)
|
|
|
|
ssh_key = load_public_key(vm_)
|
|
|
|
vcore = None
|
|
cores_per_processor = None
|
|
ram = None
|
|
fixed_instance_size_id = None
|
|
|
|
if "fixed_instance_size" in vm_:
|
|
fixed_instance_size = get_size(vm_)
|
|
fixed_instance_size_id = fixed_instance_size["id"]
|
|
elif vm_["vcore"] and vm_["cores_per_processor"] and vm_["ram"] and vm_["hdds"]:
|
|
vcore = config.get_cloud_config_value(
|
|
"vcore", vm_, __opts__, default=None, search_global=False
|
|
)
|
|
cores_per_processor = config.get_cloud_config_value(
|
|
"cores_per_processor", vm_, __opts__, default=None, search_global=False
|
|
)
|
|
ram = config.get_cloud_config_value(
|
|
"ram", vm_, __opts__, default=None, search_global=False
|
|
)
|
|
else:
|
|
raise SaltCloudConfigError(
|
|
"'fixed_instance_size' or 'vcore',"
|
|
"'cores_per_processor', 'ram', and 'hdds'"
|
|
"must be provided."
|
|
)
|
|
|
|
appliance_id = config.get_cloud_config_value(
|
|
"appliance_id", vm_, __opts__, default=None, search_global=False
|
|
)
|
|
|
|
password = config.get_cloud_config_value(
|
|
"password", vm_, __opts__, default=None, search_global=False
|
|
)
|
|
|
|
firewall_policy_id = config.get_cloud_config_value(
|
|
"firewall_policy_id", vm_, __opts__, default=None, search_global=False
|
|
)
|
|
|
|
ip_id = config.get_cloud_config_value(
|
|
"ip_id", vm_, __opts__, default=None, search_global=False
|
|
)
|
|
|
|
load_balancer_id = config.get_cloud_config_value(
|
|
"load_balancer_id", vm_, __opts__, default=None, search_global=False
|
|
)
|
|
|
|
monitoring_policy_id = config.get_cloud_config_value(
|
|
"monitoring_policy_id", vm_, __opts__, default=None, search_global=False
|
|
)
|
|
|
|
datacenter_id = config.get_cloud_config_value(
|
|
"datacenter_id", vm_, __opts__, default=None, search_global=False
|
|
)
|
|
|
|
private_network_id = config.get_cloud_config_value(
|
|
"private_network_id", vm_, __opts__, default=None, search_global=False
|
|
)
|
|
|
|
power_on = config.get_cloud_config_value(
|
|
"power_on", vm_, __opts__, default=True, search_global=False
|
|
)
|
|
|
|
public_key = config.get_cloud_config_value(
|
|
"public_key_ids", vm_, __opts__, default=True, search_global=False
|
|
)
|
|
|
|
# Contruct server object
|
|
return Server(
|
|
name=vm_["name"],
|
|
description=description,
|
|
fixed_instance_size_id=fixed_instance_size_id,
|
|
vcore=vcore,
|
|
cores_per_processor=cores_per_processor,
|
|
ram=ram,
|
|
appliance_id=appliance_id,
|
|
password=password,
|
|
power_on=power_on,
|
|
firewall_policy_id=firewall_policy_id,
|
|
ip_id=ip_id,
|
|
load_balancer_id=load_balancer_id,
|
|
monitoring_policy_id=monitoring_policy_id,
|
|
datacenter_id=datacenter_id,
|
|
rsa_key=ssh_key,
|
|
private_network_id=private_network_id,
|
|
public_key=public_key,
|
|
)
|
|
|
|
|
|
def _get_hdds(vm_):
|
|
"""
|
|
Construct VM hdds from cloud profile config
|
|
"""
|
|
_hdds = config.get_cloud_config_value(
|
|
"hdds", vm_, __opts__, default=None, search_global=False
|
|
)
|
|
|
|
hdds = []
|
|
|
|
for hdd in _hdds:
|
|
hdds.append(Hdd(size=hdd["size"], is_main=hdd["is_main"]))
|
|
|
|
return hdds
|
|
|
|
|
|
def create(vm_):
|
|
"""
|
|
Create a single VM from a data dict
|
|
"""
|
|
try:
|
|
# Check for required profile parameters before sending any API calls.
|
|
if (
|
|
vm_["profile"]
|
|
and config.is_profile_configured(
|
|
__opts__, (_get_active_provider_name() or "oneandone"), vm_["profile"]
|
|
)
|
|
is False
|
|
):
|
|
return False
|
|
except AttributeError:
|
|
pass
|
|
|
|
data = None
|
|
conn = get_conn()
|
|
hdds = []
|
|
|
|
# Assemble the composite server object.
|
|
server = _get_server(vm_)
|
|
|
|
if not bool(server.specs["hardware"]["fixed_instance_size_id"]):
|
|
# Assemble the hdds object.
|
|
hdds = _get_hdds(vm_)
|
|
|
|
__utils__["cloud.fire_event"](
|
|
"event",
|
|
"requesting instance",
|
|
"salt/cloud/{}/requesting".format(vm_["name"]),
|
|
args={"name": vm_["name"]},
|
|
sock_dir=__opts__["sock_dir"],
|
|
transport=__opts__["transport"],
|
|
)
|
|
|
|
try:
|
|
data = conn.create_server(server=server, hdds=hdds)
|
|
|
|
_wait_for_completion(conn, get_wait_timeout(vm_), data["id"])
|
|
except Exception as exc: # pylint: disable=W0703
|
|
log.error(
|
|
"Error creating %s on 1and1\n\n"
|
|
"The following exception was thrown by the 1and1 library "
|
|
"when trying to run the initial deployment: \n%s",
|
|
vm_["name"],
|
|
exc,
|
|
exc_info_on_loglevel=logging.DEBUG,
|
|
)
|
|
return False
|
|
|
|
vm_["server_id"] = data["id"]
|
|
password = data["first_password"]
|
|
|
|
def __query_node_data(vm_, data):
|
|
"""
|
|
Query node data until node becomes available.
|
|
"""
|
|
running = False
|
|
try:
|
|
data = show_instance(vm_["name"], "action")
|
|
if not data:
|
|
return False
|
|
log.debug(
|
|
"Loaded node data for %s:\nname: %s\nstate: %s",
|
|
vm_["name"],
|
|
pprint.pformat(data["name"]),
|
|
data["status"]["state"],
|
|
)
|
|
except Exception as err: # pylint: disable=broad-except
|
|
log.error(
|
|
"Failed to get nodes list: %s",
|
|
err,
|
|
# Show the trackback if the debug logging level is enabled
|
|
exc_info_on_loglevel=logging.DEBUG,
|
|
)
|
|
# Trigger a failure in the wait for IP function
|
|
return False
|
|
|
|
running = data["status"]["state"].lower() == "powered_on"
|
|
if not running:
|
|
# Still not running, trigger another iteration
|
|
return
|
|
|
|
vm_["ssh_host"] = data["ips"][0]["ip"]
|
|
|
|
return data
|
|
|
|
try:
|
|
data = salt.utils.cloud.wait_for_ip(
|
|
__query_node_data,
|
|
update_args=(vm_, data),
|
|
timeout=config.get_cloud_config_value(
|
|
"wait_for_ip_timeout", vm_, __opts__, default=10 * 60
|
|
),
|
|
interval=config.get_cloud_config_value(
|
|
"wait_for_ip_interval", vm_, __opts__, default=10
|
|
),
|
|
)
|
|
except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc:
|
|
try:
|
|
# It might be already up, let's destroy it!
|
|
destroy(vm_["name"])
|
|
except SaltCloudSystemExit:
|
|
pass
|
|
finally:
|
|
raise SaltCloudSystemExit(str(exc.message))
|
|
|
|
log.debug("VM is now running")
|
|
log.info("Created Cloud VM %s", vm_)
|
|
log.debug("%s VM creation details:\n%s", vm_, pprint.pformat(data))
|
|
|
|
__utils__["cloud.fire_event"](
|
|
"event",
|
|
"created instance",
|
|
"salt/cloud/{}/created".format(vm_["name"]),
|
|
args={
|
|
"name": vm_["name"],
|
|
"profile": vm_["profile"],
|
|
"provider": vm_["driver"],
|
|
},
|
|
sock_dir=__opts__["sock_dir"],
|
|
transport=__opts__["transport"],
|
|
)
|
|
|
|
if "ssh_host" in vm_:
|
|
vm_["password"] = password
|
|
vm_["key_filename"] = get_key_filename(vm_)
|
|
ret = __utils__["cloud.bootstrap"](vm_, __opts__)
|
|
ret.update(data)
|
|
return ret
|
|
else:
|
|
raise SaltCloudSystemExit("A valid IP address was not found.")
|
|
|
|
|
|
def destroy(name, call=None):
|
|
"""
|
|
destroy a server by name
|
|
|
|
:param name: name given to the server
|
|
:param call: call value in this case is 'action'
|
|
:return: array of booleans , true if successfully stopped and true if
|
|
successfully removed
|
|
|
|
CLI Example:
|
|
|
|
.. code-block:: bash
|
|
|
|
salt-cloud -d vm_name
|
|
|
|
"""
|
|
if call == "function":
|
|
raise SaltCloudSystemExit(
|
|
"The destroy action must be called with -d, --destroy, -a or --action."
|
|
)
|
|
|
|
__utils__["cloud.fire_event"](
|
|
"event",
|
|
"destroying instance",
|
|
f"salt/cloud/{name}/destroying",
|
|
args={"name": name},
|
|
sock_dir=__opts__["sock_dir"],
|
|
transport=__opts__["transport"],
|
|
)
|
|
|
|
conn = get_conn()
|
|
node = get_node(conn, name)
|
|
|
|
conn.delete_server(server_id=node["id"])
|
|
|
|
__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 True
|
|
|
|
|
|
def reboot(name, call=None):
|
|
"""
|
|
reboot a server by name
|
|
:param name: name given to the machine
|
|
:param call: call value in this case is 'action'
|
|
:return: true if successful
|
|
|
|
CLI Example:
|
|
|
|
.. code-block:: bash
|
|
|
|
salt-cloud -a reboot vm_name
|
|
"""
|
|
conn = get_conn()
|
|
node = get_node(conn, name)
|
|
|
|
conn.modify_server_status(server_id=node["id"], action="REBOOT")
|
|
|
|
return True
|
|
|
|
|
|
def stop(name, call=None):
|
|
"""
|
|
stop a server by name
|
|
:param name: name given to the machine
|
|
:param call: call value in this case is 'action'
|
|
:return: true if successful
|
|
|
|
CLI Example:
|
|
|
|
.. code-block:: bash
|
|
|
|
salt-cloud -a stop vm_name
|
|
"""
|
|
conn = get_conn()
|
|
node = get_node(conn, name)
|
|
|
|
conn.stop_server(server_id=node["id"])
|
|
|
|
return True
|
|
|
|
|
|
def start(name, call=None):
|
|
"""
|
|
start a server by name
|
|
:param name: name given to the machine
|
|
:param call: call value in this case is 'action'
|
|
:return: true if successful
|
|
|
|
CLI Example:
|
|
|
|
.. code-block:: bash
|
|
|
|
salt-cloud -a start vm_name
|
|
"""
|
|
conn = get_conn()
|
|
node = get_node(conn, name)
|
|
|
|
conn.start_server(server_id=node["id"])
|
|
|
|
return True
|
|
|
|
|
|
def get_node(conn, name):
|
|
"""
|
|
Return a node for the named VM
|
|
"""
|
|
for node in conn.list_servers(per_page=1000):
|
|
if node["name"] == name:
|
|
return node
|
|
|
|
|
|
def get_key_filename(vm_):
|
|
"""
|
|
Check SSH private key file and return absolute path if exists.
|
|
"""
|
|
key_filename = config.get_cloud_config_value(
|
|
"ssh_private_key", vm_, __opts__, search_global=False, default=None
|
|
)
|
|
if key_filename is not None:
|
|
key_filename = os.path.expanduser(key_filename)
|
|
if not os.path.isfile(key_filename):
|
|
raise SaltCloudConfigError(
|
|
f"The defined ssh_private_key '{key_filename}' does not exist"
|
|
)
|
|
|
|
return key_filename
|
|
|
|
|
|
def load_public_key(vm_):
|
|
"""
|
|
Load the public key file if exists.
|
|
"""
|
|
public_key_filename = config.get_cloud_config_value(
|
|
"ssh_public_key", vm_, __opts__, search_global=False, default=None
|
|
)
|
|
if public_key_filename is not None:
|
|
public_key_filename = os.path.expanduser(public_key_filename)
|
|
if not os.path.isfile(public_key_filename):
|
|
raise SaltCloudConfigError(
|
|
"The defined ssh_public_key '{}' does not exist".format(
|
|
public_key_filename
|
|
)
|
|
)
|
|
|
|
with salt.utils.files.fopen(public_key_filename, "r") as public_key:
|
|
key = salt.utils.stringutils.to_unicode(public_key.read().replace("\n", ""))
|
|
|
|
return key
|
|
|
|
|
|
def get_wait_timeout(vm_):
|
|
"""
|
|
Return the wait_for_timeout for resource provisioning.
|
|
"""
|
|
return config.get_cloud_config_value(
|
|
"wait_for_timeout", vm_, __opts__, default=15 * 60, search_global=False
|
|
)
|
|
|
|
|
|
def _wait_for_completion(conn, wait_timeout, server_id):
|
|
"""
|
|
Poll request status until resource is provisioned.
|
|
"""
|
|
wait_timeout = time.time() + wait_timeout
|
|
while wait_timeout > time.time():
|
|
time.sleep(5)
|
|
|
|
server = conn.get_server(server_id)
|
|
server_state = server["status"]["state"].lower()
|
|
|
|
if server_state == "powered_on":
|
|
return
|
|
elif server_state == "failed":
|
|
raise Exception(f"Server creation failed for {server_id}")
|
|
elif server_state in ("active", "enabled", "deploying", "configuring"):
|
|
continue
|
|
else:
|
|
raise Exception(f"Unknown server state {server_state}")
|
|
raise Exception(f"Timed out waiting for server create completion for {server_id}")
|