mirror of
https://github.com/valitydev/salt.git
synced 2024-11-06 16:45:27 +00:00
Imported from https://github.com/dddrew/salt-dimensiondata (credits @dddrew) worked on PEP8 compliance.
This commit is contained in:
parent
e202ce1ce0
commit
e6601a81da
548
salt/cloud/clouds/dimensiondata.py
Normal file
548
salt/cloud/clouds/dimensiondata.py
Normal file
@ -0,0 +1,548 @@
|
||||
'''
|
||||
Dimension Data Cloud Module
|
||||
===========================
|
||||
|
||||
This is a cloud module for the Dimension Data Cloud,
|
||||
using the existing Libcloud driver for Dimension Data.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# Note: This example is for /etc/salt/cloud.providers
|
||||
# or any file in the
|
||||
# /etc/salt/cloud.providers.d/ directory.
|
||||
|
||||
my-dimensiondata-config:
|
||||
user_id: my_username
|
||||
key: myPassword!
|
||||
region: dd-na
|
||||
driver: dimensiondata
|
||||
|
||||
'''
|
||||
|
||||
# Import python libs
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
import socket
|
||||
import pprint
|
||||
|
||||
# Import libcloud
|
||||
try:
|
||||
from libcloud.compute.base import NodeState
|
||||
from libcloud.compute.base import NodeAuthPassword
|
||||
from libcloud.loadbalancer.base import Member
|
||||
from libcloud.loadbalancer.types import Provider as Provider_lb
|
||||
from libcloud.loadbalancer.providers import get_driver as get_driver_lb
|
||||
|
||||
HAS_LIBCLOUD = True
|
||||
except ImportError:
|
||||
HAS_LIBCLOUD = False
|
||||
|
||||
# Import generic libcloud functions
|
||||
from salt.cloud.libcloudfuncs import *
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
|
||||
# Import salt.cloud libs
|
||||
import salt.utils.cloud
|
||||
import salt.utils.pycrypto as sup
|
||||
import salt.config as config
|
||||
from salt.utils import namespaced_function
|
||||
from salt.exceptions import (
|
||||
SaltCloudConfigError,
|
||||
SaltCloudNotFound,
|
||||
SaltCloudSystemExit,
|
||||
SaltCloudExecutionFailure,
|
||||
SaltCloudExecutionTimeout
|
||||
)
|
||||
|
||||
# Import netaddr IP matching
|
||||
try:
|
||||
from netaddr import all_matching_cidrs
|
||||
HAS_NETADDR = True
|
||||
except ImportError:
|
||||
HAS_NETADDR = False
|
||||
|
||||
# Get logging started
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
__virtualname__ = 'dimensiondata'
|
||||
|
||||
|
||||
# Some of the libcloud functions need to be in the same namespace as the
|
||||
# functions defined in the module, so we create new function objects inside
|
||||
# this module namespace
|
||||
get_size = namespaced_function(get_size, globals())
|
||||
get_image = namespaced_function(get_image, globals())
|
||||
avail_locations = namespaced_function(avail_locations, globals())
|
||||
avail_images = namespaced_function(avail_images, globals())
|
||||
avail_sizes = namespaced_function(avail_sizes, globals())
|
||||
script = namespaced_function(script, globals())
|
||||
destroy = namespaced_function(destroy, globals())
|
||||
reboot = namespaced_function(reboot, globals())
|
||||
list_nodes = namespaced_function(list_nodes, globals())
|
||||
list_nodes_full = namespaced_function(list_nodes_full, globals())
|
||||
list_nodes_select = namespaced_function(list_nodes_select, globals())
|
||||
show_instance = namespaced_function(show_instance, globals())
|
||||
get_node = namespaced_function(get_node, globals())
|
||||
|
||||
|
||||
# Only load in this module is the DIMENSIONDATA configurations are in place
|
||||
def __virtual__():
|
||||
'''
|
||||
Set up the libcloud functions and check for GCE configurations.
|
||||
'''
|
||||
if get_configured_provider() is False:
|
||||
return False
|
||||
|
||||
if get_dependencies() is False:
|
||||
return False
|
||||
|
||||
for provider, details in six.iteritems(__opts__['providers']):
|
||||
if 'dimensiondata' not in details:
|
||||
continue
|
||||
|
||||
return __virtualname__
|
||||
|
||||
|
||||
def get_configured_provider():
|
||||
'''
|
||||
Return the first configured instance.
|
||||
'''
|
||||
return config.is_provider_configured(
|
||||
__opts__,
|
||||
__active_provider_name__ or 'dimensiondata',
|
||||
('user_id', 'key', 'region')
|
||||
)
|
||||
|
||||
|
||||
def get_dependencies():
|
||||
'''
|
||||
Warn if dependencies aren't met.
|
||||
'''
|
||||
deps = {
|
||||
'libcloud': HAS_LIBCLOUD,
|
||||
'netaddr': HAS_NETADDR
|
||||
}
|
||||
return config.check_driver_dependencies(
|
||||
__virtualname__,
|
||||
deps
|
||||
)
|
||||
|
||||
|
||||
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__,
|
||||
__active_provider_name__ or 'dimensiondata',
|
||||
vm_['profile']) is False:
|
||||
return False
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# Since using "provider: <provider-engine>" is deprecated, alias provider
|
||||
# to use driver: "driver: <provider-engine>"
|
||||
if 'provider' in vm_:
|
||||
vm_['driver'] = vm_.pop('provider')
|
||||
|
||||
salt.utils.cloud.fire_event(
|
||||
'event',
|
||||
'starting create',
|
||||
'salt/cloud/{0}/creating'.format(vm_['name']),
|
||||
{
|
||||
'name': vm_['name'],
|
||||
'profile': vm_['profile'],
|
||||
'provider': vm_['driver'],
|
||||
},
|
||||
transport=__opts__['transport']
|
||||
)
|
||||
|
||||
|
||||
log.info('Creating Cloud VM {0}'.format(vm_['name']))
|
||||
conn = get_conn()
|
||||
rootPw = NodeAuthPassword(vm_['auth'])
|
||||
|
||||
try:
|
||||
location = conn.ex_get_location_by_id(vm_['location'])
|
||||
images = conn.list_images(location=location)
|
||||
image = [x for x in images if x.id == vm_['image']][0]
|
||||
networks = conn.ex_list_network_domains(location=location)
|
||||
network_domain = [y for y in networks if y.name ==
|
||||
vm_['network_domain']][0]
|
||||
# Use the first VLAN in the network domain
|
||||
vlan = conn.ex_list_vlans(location=location,
|
||||
network_domain=network_domain)[0]
|
||||
kwargs = {
|
||||
'name': vm_['name'],
|
||||
'image': image,
|
||||
'auth': rootPw,
|
||||
'ex_description': vm_['description'],
|
||||
'ex_network_domain': network_domain,
|
||||
'ex_vlan': vlan,
|
||||
'ex_is_started': vm_['is_started']
|
||||
}
|
||||
data = conn.create_node(**kwargs)
|
||||
except Exception as exc:
|
||||
log.error(
|
||||
'Error creating %s on DIMENSIONDATA\n\n'
|
||||
'The following exception was thrown by libcloud when trying to '
|
||||
'run the initial deployment: \n%s' %
|
||||
(vm_['name'], exc),
|
||||
exc_info_on_loglevel=logging.DEBUG
|
||||
)
|
||||
return False
|
||||
|
||||
def __query_node_data(vm_, data):
|
||||
running = False
|
||||
try:
|
||||
node = show_instance(vm_['name'], 'action')
|
||||
running = (node['state'] == NodeState.RUNNING)
|
||||
log.debug(
|
||||
'Loaded node data for %s:\nname: %s\nstate: %s' % (
|
||||
vm_['name'],
|
||||
pprint.pformat(node['name']),
|
||||
node['state']
|
||||
)
|
||||
)
|
||||
except Exception as err:
|
||||
log.error(
|
||||
'Failed to get nodes list: %s' % (
|
||||
err
|
||||
),
|
||||
# Show the traceback if the debug logging level is enabled
|
||||
exc_info_on_loglevel=logging.DEBUG
|
||||
)
|
||||
# Trigger a failure in the wait for IP function
|
||||
return False
|
||||
|
||||
if not running:
|
||||
# Still not running, trigger another iteration
|
||||
return
|
||||
|
||||
private = node['private_ips']
|
||||
public = node['public_ips']
|
||||
|
||||
if private and not public:
|
||||
log.warn(
|
||||
'Private IPs returned, but not public... Checking for '
|
||||
'misidentified IPs'
|
||||
)
|
||||
for private_ip in private:
|
||||
private_ip = preferred_ip(vm_, [private_ip])
|
||||
if salt.utils.cloud.is_public_ip(private_ip):
|
||||
log.warn('%s is a public IP' % (private_ip))
|
||||
data.public_ips.append(private_ip)
|
||||
else:
|
||||
log.warn('% is a private IP' % (private_ip))
|
||||
if private_ip not in data.private_ips:
|
||||
data.private_ips.append(private_ip)
|
||||
|
||||
if ssh_interface(vm_) == 'private_ips' and data.private_ips:
|
||||
return data
|
||||
|
||||
if private:
|
||||
data.private_ips = private
|
||||
if ssh_interface(vm_) == 'private_ips':
|
||||
return data
|
||||
|
||||
if public:
|
||||
data.public_ips = public
|
||||
if ssh_interface(vm_) != 'private_ips':
|
||||
return data
|
||||
|
||||
log.debug('DATA')
|
||||
log.debug(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=25 * 60),
|
||||
interval=config.get_cloud_config_value(
|
||||
'wait_for_ip_interval', vm_, __opts__, default=30),
|
||||
max_failures=config.get_cloud_config_value(
|
||||
'wait_for_ip_max_failures', vm_, __opts__, default=60),
|
||||
)
|
||||
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))
|
||||
|
||||
log.debug('VM is now running')
|
||||
if ssh_interface(vm_) == 'private_ips':
|
||||
ip_address = preferred_ip(vm_, data.private_ips)
|
||||
else:
|
||||
ip_address = preferred_ip(vm_, data.public_ips)
|
||||
log.debug('Using IP address %s' % (ip_address))
|
||||
|
||||
if salt.utils.cloud.get_salt_interface(vm_, __opts__) == 'private_ips':
|
||||
salt_ip_address = preferred_ip(vm_, data.private_ips)
|
||||
log.info('Salt interface set to: %s' % (salt_ip_address))
|
||||
else:
|
||||
salt_ip_address = preferred_ip(vm_, data.public_ips)
|
||||
log.debug('Salt interface set to: %s' % (salt_ip_address))
|
||||
|
||||
if not ip_address:
|
||||
raise SaltCloudSystemExit(
|
||||
'No IP addresses could be found.'
|
||||
)
|
||||
|
||||
vm_['salt_host'] = salt_ip_address
|
||||
vm_['ssh_host'] = ip_address
|
||||
vm_['password'] = vm_['auth']
|
||||
|
||||
ret = salt.utils.cloud.bootstrap(vm_, __opts__)
|
||||
|
||||
ret.update(data.__dict__)
|
||||
|
||||
if 'password' in data.extra:
|
||||
del data.extra['password']
|
||||
|
||||
log.info('Created Cloud VM \'{0[name]}\''.format(vm_))
|
||||
log.debug(
|
||||
'\'{0[name]}\' VM creation details:\n{1}'.format(
|
||||
vm_, pprint.pformat(data.__dict__)
|
||||
)
|
||||
)
|
||||
|
||||
salt.utils.cloud.fire_event(
|
||||
'event',
|
||||
'created instance',
|
||||
'salt/cloud/{0}/created'.format(vm_['name']),
|
||||
{
|
||||
'name': vm_['name'],
|
||||
'profile': vm_['profile'],
|
||||
'provider': vm_['driver'],
|
||||
},
|
||||
transport=__opts__['transport']
|
||||
)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def create_lb(kwargs=None, call=None):
|
||||
'''
|
||||
Create a load-balancer configuration.
|
||||
CLI Example:
|
||||
.. code-block:: bash
|
||||
salt-cloud -f create_lb dimensiondata \
|
||||
name=dev-lb port=80 protocol=http \
|
||||
members=w1,w2,w3 algorithm=ROUND_ROBIN
|
||||
'''
|
||||
conn = get_conn()
|
||||
if call != 'function':
|
||||
raise SaltCloudSystemExit(
|
||||
'The create_lb function must be called with -f or --function.'
|
||||
)
|
||||
|
||||
if not kwargs or 'name' not in kwargs:
|
||||
log.error(
|
||||
'A name must be specified when creating a health check.'
|
||||
)
|
||||
return False
|
||||
if 'port' not in kwargs:
|
||||
log.error(
|
||||
'A port or port-range must be specified for the load-balancer.'
|
||||
)
|
||||
return False
|
||||
if 'networkdomain' not in kwargs:
|
||||
log.error(
|
||||
'A network domain must be specified for the load-balancer.'
|
||||
)
|
||||
return False
|
||||
if 'members' in kwargs:
|
||||
members = []
|
||||
ip = ""
|
||||
membersList = kwargs.get('members').split(',')
|
||||
log.debug('MemberList: {0}'.format(membersList))
|
||||
for member in membersList:
|
||||
try:
|
||||
log.debug('Member: {0}'.format(member))
|
||||
node = get_node(conn,member)
|
||||
log.debug('Node: {0}'.format(node))
|
||||
ip = node.private_ips[0]
|
||||
except Exception as err:
|
||||
log.error(
|
||||
'Failed to get node ip: {0}'.format(
|
||||
err
|
||||
),
|
||||
# Show the traceback if the debug logging level is enabled
|
||||
exc_info_on_loglevel=logging.DEBUG
|
||||
)
|
||||
members.append(Member(ip, ip, kwargs['port']))
|
||||
else:
|
||||
members = None
|
||||
log.debug('Members: {0}'.format(members))
|
||||
|
||||
networkdomain = kwargs['networkdomain']
|
||||
name = kwargs['name']
|
||||
port = kwargs['port']
|
||||
protocol = kwargs.get('protocol', None)
|
||||
algorithm = kwargs.get('algorithm', None)
|
||||
|
||||
lb_conn = get_lb_conn(conn)
|
||||
network_domains = conn.ex_list_network_domains()
|
||||
network_domain = [y for y in network_domains if y.name == networkdomain][0]
|
||||
|
||||
log.debug('Network Domain: {0}'.format(network_domain.id))
|
||||
lb_conn.ex_set_current_network_domain(network_domain.id)
|
||||
|
||||
salt.utils.cloud.fire_event(
|
||||
'event',
|
||||
'create load_balancer',
|
||||
'salt/cloud/loadbalancer/creating',
|
||||
kwargs,
|
||||
transport=__opts__['transport']
|
||||
)
|
||||
|
||||
lb = lb_conn.create_balancer(
|
||||
name, port, protocol, algorithm, members
|
||||
)
|
||||
|
||||
salt.utils.cloud.fire_event(
|
||||
'event',
|
||||
'created load_balancer',
|
||||
'salt/cloud/loadbalancer/created',
|
||||
kwargs,
|
||||
transport=__opts__['transport']
|
||||
)
|
||||
return _expand_balancer(lb)
|
||||
|
||||
|
||||
def _expand_balancer(lb):
|
||||
'''
|
||||
Convert the libcloud load-balancer object into something more serializable.
|
||||
'''
|
||||
ret = {}
|
||||
ret.update(lb.__dict__)
|
||||
return ret
|
||||
|
||||
|
||||
def preferred_ip(vm_, ips):
|
||||
'''
|
||||
Return the preferred Internet protocol. Either 'ipv4' (default) or 'ipv6'.
|
||||
'''
|
||||
proto = config.get_cloud_config_value(
|
||||
'protocol', vm_, __opts__, default='ipv4', search_global=False
|
||||
)
|
||||
family = socket.AF_INET
|
||||
if proto == 'ipv6':
|
||||
family = socket.AF_INET6
|
||||
for ip in ips:
|
||||
try:
|
||||
socket.inet_pton(family, ip)
|
||||
return ip
|
||||
except Exception:
|
||||
continue
|
||||
return False
|
||||
|
||||
|
||||
def ssh_interface(vm_):
|
||||
'''
|
||||
Return the ssh_interface type to connect to. Either 'public_ips' (default)
|
||||
or 'private_ips'.
|
||||
'''
|
||||
return config.get_cloud_config_value(
|
||||
'ssh_interface', vm_, __opts__, default='public_ips',
|
||||
search_global=False
|
||||
)
|
||||
|
||||
|
||||
def stop(name, call=None):
|
||||
'''
|
||||
Stop a VM in DimensionData.
|
||||
name
|
||||
The name of the VM to stop.
|
||||
CLI Example:
|
||||
.. code-block:: bash
|
||||
salt-cloud -a stop vm_name
|
||||
'''
|
||||
conn = get_conn()
|
||||
node = get_node(conn, name)
|
||||
log.debug('Node of Cloud VM: {0}'.format(node))
|
||||
|
||||
status = conn.ex_shutdown_graceful(node)
|
||||
log.debug('Status of Cloud VM: {0}'.format(status))
|
||||
|
||||
return status
|
||||
|
||||
def start(name, call=None):
|
||||
'''
|
||||
Stop a VM in DimensionData.
|
||||
name
|
||||
The name of the VM to stop.
|
||||
CLI Example:
|
||||
.. code-block:: bash
|
||||
salt-cloud -a stop vm_name
|
||||
'''
|
||||
|
||||
conn = get_conn()
|
||||
node = get_node(conn, name)
|
||||
log.debug('Node of Cloud VM: {0}'.format(node))
|
||||
|
||||
status = conn.ex_start_node(node)
|
||||
log.debug('Status of Cloud VM: {0}'.format(status))
|
||||
|
||||
return status
|
||||
|
||||
|
||||
def get_conn():
|
||||
'''
|
||||
Return a conn object for the passed VM data
|
||||
'''
|
||||
|
||||
vm_ = get_configured_provider()
|
||||
driver = get_driver(Provider.DIMENSIONDATA)
|
||||
|
||||
region = config.get_cloud_config_value(
|
||||
'region', vm_, __opts__
|
||||
)
|
||||
|
||||
user_id = config.get_cloud_config_value(
|
||||
'user_id', vm_, __opts__
|
||||
)
|
||||
key = config.get_cloud_config_value(
|
||||
'key', vm_, __opts__
|
||||
)
|
||||
|
||||
if key is not None:
|
||||
log.debug('DimensionData authenticating using password')
|
||||
|
||||
return driver(
|
||||
user_id,
|
||||
key,
|
||||
region=region
|
||||
)
|
||||
|
||||
|
||||
def get_lb_conn(dd_driver=None):
|
||||
'''
|
||||
Return a load-balancer conn object
|
||||
'''
|
||||
vm_ = get_configured_provider()
|
||||
driver = get_driver(Provider.DIMENSIONDATA)
|
||||
|
||||
region = config.get_cloud_config_value(
|
||||
'region', vm_, __opts__
|
||||
)
|
||||
|
||||
user_id = config.get_cloud_config_value(
|
||||
'user_id', vm_, __opts__
|
||||
)
|
||||
key = config.get_cloud_config_value(
|
||||
'key', vm_, __opts__
|
||||
)
|
||||
if not dd_driver:
|
||||
raise SaltCloudSystemExit(
|
||||
'Missing dimensiondata_driver for get_lb_conn method.'
|
||||
)
|
||||
return get_driver_lb(Provider_lb.DIMENSIONDATA)(user_id, key, region=region)
|
Loading…
Reference in New Issue
Block a user