mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
441 lines
12 KiB
Python
441 lines
12 KiB
Python
"""
|
|
CenturyLink Cloud Module
|
|
========================
|
|
|
|
.. versionadded:: 2018.3
|
|
|
|
The CLC cloud module allows you to manage CLC Via the CLC SDK.
|
|
|
|
:codeauthor: Stephan Looney <slooney@stephanlooney.com>
|
|
|
|
|
|
Dependencies
|
|
============
|
|
|
|
- clc-sdk Python Module
|
|
- flask
|
|
|
|
CLC SDK
|
|
-------
|
|
|
|
clc-sdk can be installed via pip:
|
|
|
|
.. code-block:: bash
|
|
|
|
pip install clc-sdk
|
|
|
|
.. note::
|
|
For sdk reference see: https://github.com/CenturyLinkCloud/clc-python-sdk
|
|
|
|
Flask
|
|
-----
|
|
|
|
flask can be installed via pip:
|
|
|
|
.. code-block:: bash
|
|
|
|
pip install flask
|
|
|
|
Configuration
|
|
=============
|
|
|
|
To use this module: set up the clc-sdk, user, password, key in the
|
|
cloud configuration at
|
|
``/etc/salt/cloud.providers`` or ``/etc/salt/cloud.providers.d/clc.conf``:
|
|
|
|
.. code-block:: yaml
|
|
|
|
my-clc-config:
|
|
driver: clc
|
|
user: 'web-user'
|
|
password: 'verybadpass'
|
|
token: ''
|
|
token_pass:''
|
|
accountalias: 'ACT'
|
|
.. note::
|
|
|
|
The ``provider`` parameter in cloud provider configuration was renamed to ``driver``.
|
|
This change was made to avoid confusion with the ``provider`` parameter that is
|
|
used in cloud profile configuration. Cloud provider configuration now uses ``driver``
|
|
to refer to the salt-cloud driver that provides the underlying functionality to
|
|
connect to a cloud provider, while cloud profile configuration continues to use
|
|
``provider`` to refer to the cloud provider configuration that you define.
|
|
|
|
"""
|
|
|
|
import importlib
|
|
import logging
|
|
import time
|
|
|
|
import salt.config as config
|
|
import salt.utils.json
|
|
from salt.exceptions import SaltCloudSystemExit
|
|
|
|
# Attempt to import clc-sdk lib
|
|
try:
|
|
# when running this in linode's Ubuntu 16.x version the following line is required
|
|
# to get the clc sdk libraries to load
|
|
importlib.import_module("clc")
|
|
import clc
|
|
|
|
HAS_CLC = True
|
|
except ImportError:
|
|
HAS_CLC = False
|
|
# Disable InsecureRequestWarning generated on python > 2.6
|
|
try:
|
|
from requests.packages.urllib3 import ( # pylint: disable=no-name-in-module
|
|
disable_warnings,
|
|
)
|
|
|
|
disable_warnings()
|
|
except Exception: # pylint: disable=broad-except
|
|
pass
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
__virtualname__ = "clc"
|
|
|
|
|
|
# Only load in this module if the CLC configurations are in place
|
|
def __virtual__():
|
|
"""
|
|
Check for CLC configuration and if required libs are available.
|
|
"""
|
|
if get_configured_provider() is False or 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 config.is_provider_configured(
|
|
__opts__,
|
|
_get_active_provider_name() or __virtualname__,
|
|
(
|
|
"token",
|
|
"token_pass",
|
|
"user",
|
|
"password",
|
|
),
|
|
)
|
|
|
|
|
|
def get_dependencies():
|
|
"""
|
|
Warn if dependencies aren't met.
|
|
"""
|
|
deps = {
|
|
"clc": HAS_CLC,
|
|
}
|
|
return config.check_driver_dependencies(__virtualname__, deps)
|
|
|
|
|
|
def get_creds():
|
|
user = config.get_cloud_config_value(
|
|
"user", get_configured_provider(), __opts__, search_global=False
|
|
)
|
|
password = config.get_cloud_config_value(
|
|
"password", get_configured_provider(), __opts__, search_global=False
|
|
)
|
|
accountalias = config.get_cloud_config_value(
|
|
"accountalias", get_configured_provider(), __opts__, search_global=False
|
|
)
|
|
token = config.get_cloud_config_value(
|
|
"token", get_configured_provider(), __opts__, search_global=False
|
|
)
|
|
token_pass = config.get_cloud_config_value(
|
|
"token_pass", get_configured_provider(), __opts__, search_global=False
|
|
)
|
|
creds = {
|
|
"user": user,
|
|
"password": password,
|
|
"token": token,
|
|
"token_pass": token_pass,
|
|
"accountalias": accountalias,
|
|
}
|
|
return creds
|
|
|
|
|
|
def list_nodes_full(call=None, for_output=True):
|
|
"""
|
|
Return a list of the VMs that are on the provider
|
|
"""
|
|
if call == "action":
|
|
raise SaltCloudSystemExit(
|
|
"The list_nodes_full function must be called with -f or --function."
|
|
)
|
|
creds = get_creds()
|
|
clc.v1.SetCredentials(creds["token"], creds["token_pass"])
|
|
servers_raw = clc.v1.Server.GetServers(location=None)
|
|
servers_raw = salt.utils.json.dumps(servers_raw)
|
|
servers = salt.utils.json.loads(servers_raw)
|
|
return servers
|
|
|
|
|
|
def get_queue_data(call=None, for_output=True):
|
|
creds = get_creds()
|
|
clc.v1.SetCredentials(creds["token"], creds["token_pass"])
|
|
cl_queue = clc.v1.Queue.List()
|
|
return cl_queue
|
|
|
|
|
|
def get_monthly_estimate(call=None, for_output=True):
|
|
"""
|
|
Return a list of the VMs that are on the provider
|
|
"""
|
|
creds = get_creds()
|
|
clc.v1.SetCredentials(creds["token"], creds["token_pass"])
|
|
if call == "action":
|
|
raise SaltCloudSystemExit(
|
|
"The list_nodes_full function must be called with -f or --function."
|
|
)
|
|
try:
|
|
billing_raw = clc.v1.Billing.GetAccountSummary(alias=creds["accountalias"])
|
|
billing_raw = salt.utils.json.dumps(billing_raw)
|
|
billing = salt.utils.json.loads(billing_raw)
|
|
billing = round(billing["MonthlyEstimate"], 2)
|
|
return {"Monthly Estimate": billing}
|
|
except RuntimeError:
|
|
return {"Monthly Estimate": 0}
|
|
|
|
|
|
def get_month_to_date(call=None, for_output=True):
|
|
"""
|
|
Return a list of the VMs that are on the provider
|
|
"""
|
|
creds = get_creds()
|
|
clc.v1.SetCredentials(creds["token"], creds["token_pass"])
|
|
if call == "action":
|
|
raise SaltCloudSystemExit(
|
|
"The list_nodes_full function must be called with -f or --function."
|
|
)
|
|
try:
|
|
billing_raw = clc.v1.Billing.GetAccountSummary(alias=creds["accountalias"])
|
|
billing_raw = salt.utils.json.dumps(billing_raw)
|
|
billing = salt.utils.json.loads(billing_raw)
|
|
billing = round(billing["MonthToDateTotal"], 2)
|
|
return {"Month To Date": billing}
|
|
except RuntimeError:
|
|
return 0
|
|
|
|
|
|
def get_server_alerts(call=None, for_output=True, **kwargs):
|
|
"""
|
|
Return a list of alerts from CLC as reported by their infra
|
|
"""
|
|
for key, value in kwargs.items():
|
|
servername = ""
|
|
if key == "servername":
|
|
servername = value
|
|
creds = get_creds()
|
|
clc.v2.SetCredentials(creds["user"], creds["password"])
|
|
alerts = clc.v2.Server(servername).Alerts()
|
|
return alerts
|
|
|
|
|
|
def get_group_estimate(call=None, for_output=True, **kwargs):
|
|
"""
|
|
Return a list of the VMs that are on the provider
|
|
usage: "salt-cloud -f get_group_estimate clc group=Dev location=VA1"
|
|
"""
|
|
for key, value in kwargs.items():
|
|
group = ""
|
|
location = ""
|
|
if key == "group":
|
|
group = value
|
|
if key == "location":
|
|
location = value
|
|
creds = get_creds()
|
|
clc.v1.SetCredentials(creds["token"], creds["token_pass"])
|
|
if call == "action":
|
|
raise SaltCloudSystemExit(
|
|
"The list_nodes_full function must be called with -f or --function."
|
|
)
|
|
try:
|
|
billing_raw = clc.v1.Billing.GetGroupEstimate(
|
|
group=group, alias=creds["accountalias"], location=location
|
|
)
|
|
billing_raw = salt.utils.json.dumps(billing_raw)
|
|
billing = salt.utils.json.loads(billing_raw)
|
|
estimate = round(billing["MonthlyEstimate"], 2)
|
|
month_to_date = round(billing["MonthToDate"], 2)
|
|
return {"Monthly Estimate": estimate, "Month to Date": month_to_date}
|
|
except RuntimeError:
|
|
return 0
|
|
|
|
|
|
def avail_images(call=None):
|
|
"""
|
|
returns a list of images available to you
|
|
"""
|
|
all_servers = list_nodes_full()
|
|
templates = {}
|
|
for server in all_servers:
|
|
if server["IsTemplate"]:
|
|
templates.update({"Template Name": server["Name"]})
|
|
return templates
|
|
|
|
|
|
def avail_locations(call=None):
|
|
"""
|
|
returns a list of locations available to you
|
|
"""
|
|
creds = get_creds()
|
|
clc.v1.SetCredentials(creds["token"], creds["token_pass"])
|
|
locations = clc.v1.Account.GetLocations()
|
|
return locations
|
|
|
|
|
|
def avail_sizes(call=None):
|
|
"""
|
|
use templates for this
|
|
"""
|
|
return {"Sizes": "Sizes are built into templates. Choose appropriate template"}
|
|
|
|
|
|
def get_build_status(req_id, nodename):
|
|
"""
|
|
get the build status from CLC to make sure we don't return to early
|
|
"""
|
|
counter = 0
|
|
req_id = str(req_id)
|
|
while counter < 10:
|
|
queue = clc.v1.Blueprint.GetStatus(request_id=(req_id))
|
|
if queue["PercentComplete"] == 100:
|
|
server_name = queue["Servers"][0]
|
|
creds = get_creds()
|
|
clc.v2.SetCredentials(creds["user"], creds["password"])
|
|
ip_addresses = clc.v2.Server(server_name).ip_addresses
|
|
internal_ip_address = ip_addresses[0]["internal"]
|
|
return internal_ip_address
|
|
else:
|
|
counter = counter + 1
|
|
log.info(
|
|
"Creating Cloud VM %s Time out in %s minutes",
|
|
nodename,
|
|
str(10 - counter),
|
|
)
|
|
time.sleep(60)
|
|
|
|
|
|
def create(vm_):
|
|
"""
|
|
get the system build going
|
|
"""
|
|
creds = get_creds()
|
|
clc.v1.SetCredentials(creds["token"], creds["token_pass"])
|
|
cloud_profile = config.is_provider_configured(
|
|
__opts__, _get_active_provider_name() or __virtualname__, ("token",)
|
|
)
|
|
group = config.get_cloud_config_value(
|
|
"group",
|
|
vm_,
|
|
__opts__,
|
|
search_global=False,
|
|
default=None,
|
|
)
|
|
name = vm_["name"]
|
|
description = config.get_cloud_config_value(
|
|
"description",
|
|
vm_,
|
|
__opts__,
|
|
search_global=False,
|
|
default=None,
|
|
)
|
|
ram = config.get_cloud_config_value(
|
|
"ram",
|
|
vm_,
|
|
__opts__,
|
|
search_global=False,
|
|
default=None,
|
|
)
|
|
backup_level = config.get_cloud_config_value(
|
|
"backup_level",
|
|
vm_,
|
|
__opts__,
|
|
search_global=False,
|
|
default=None,
|
|
)
|
|
template = config.get_cloud_config_value(
|
|
"template",
|
|
vm_,
|
|
__opts__,
|
|
search_global=False,
|
|
default=None,
|
|
)
|
|
password = config.get_cloud_config_value(
|
|
"password",
|
|
vm_,
|
|
__opts__,
|
|
search_global=False,
|
|
default=None,
|
|
)
|
|
cpu = config.get_cloud_config_value(
|
|
"cpu",
|
|
vm_,
|
|
__opts__,
|
|
search_global=False,
|
|
default=None,
|
|
)
|
|
network = config.get_cloud_config_value(
|
|
"network",
|
|
vm_,
|
|
__opts__,
|
|
search_global=False,
|
|
default=None,
|
|
)
|
|
location = config.get_cloud_config_value(
|
|
"location",
|
|
vm_,
|
|
__opts__,
|
|
search_global=False,
|
|
default=None,
|
|
)
|
|
if len(name) > 6:
|
|
name = name[0:6]
|
|
if len(password) < 9:
|
|
password = ""
|
|
clc_return = clc.v1.Server.Create(
|
|
alias=None,
|
|
location=(location),
|
|
name=(name),
|
|
template=(template),
|
|
cpu=(cpu),
|
|
ram=(ram),
|
|
backup_level=(backup_level),
|
|
group=(group),
|
|
network=(network),
|
|
description=(description),
|
|
password=(password),
|
|
)
|
|
req_id = clc_return["RequestID"]
|
|
vm_["ssh_host"] = get_build_status(req_id, name)
|
|
__utils__["cloud.fire_event"](
|
|
"event",
|
|
"waiting for ssh",
|
|
"salt/cloud/{}/waiting_for_ssh".format(name),
|
|
sock_dir=__opts__["sock_dir"],
|
|
args={"ip_address": vm_["ssh_host"]},
|
|
transport=__opts__["transport"],
|
|
)
|
|
|
|
# Bootstrap!
|
|
ret = __utils__["cloud.bootstrap"](vm_, __opts__)
|
|
return_message = {"Server Name": name, "IP Address": vm_["ssh_host"]}
|
|
ret.update(return_message)
|
|
return return_message
|
|
|
|
|
|
def destroy(name, call=None):
|
|
"""
|
|
destroy the vm
|
|
"""
|
|
return {"status": "destroying must be done via https://control.ctl.io at this time"}
|