Skip to content

Instantly share code, notes, and snippets.

@jochumdev
Last active February 27, 2018 20:49
Show Gist options
  • Save jochumdev/e8ba889179509a6c79f3d6103322f1a9 to your computer and use it in GitHub Desktop.
Save jochumdev/e8ba889179509a6c79f3d6103322f1a9 to your computer and use it in GitHub Desktop.
# -*- coding: utf-8 -*-
'''
LXD Cloud Module
================
.. versionadded:: unknown
`LXD(1)`__ is a container "hypervisor". This execution module provides
several functions to help manage it and its containers.
.. note:
- `pylxd(2)`__ version >=2.2.5 is required to let this work,
currently only available via pip.
To install on Ubuntu:
$ apt-get install libssl-dev python-pip
$ pip install -U pylxd
.. __: https://linuxcontainers.org/lxd/
.. __: https://github.com/lxc/pylxd/blob/master/doc/source/installation.rst
Configuration
=============
To use this module, set up the LXD URL, ssl-cert, ssl-key and password in the
cloud configuration at
``/etc/salt/cloud.providers`` or ``/etc/salt/cloud.providers.d/lxd.conf``:
.. code-block:: yaml
my-lxd-config:
driver: lxd
url: https://srv01:8443
key: /etc/salt/lxd_client.key
cert: /etc/salt/lxd_client.crt
password: 'verybadpass'
verify_cert: False
Test if the connection can be made to the lxd server using
the specified credentials inside ``/etc/salt/cloud.providers``
or ``/etc/salt/cloud.providers.d/lxd.conf``
.. code-block:: bash
salt-cloud -f test_lxd_connection my-lxd-config
:maintainer: René Jochum <rene@jochums.at>
:maturity: new
:depends: python-pylxd
:platform: Linux
'''
# Import python libs
from __future__ import absolute_import, print_function, unicode_literals
import os
from distutils.version import LooseVersion
# Import salt libs
from salt.exceptions import CommandExecutionError
from salt.exceptions import SaltInvocationError
import salt.ext.six as six
# Import salt cloud libs
import salt.config as config
# Import 3rd-party libs
try:
import pylxd
HAS_PYLXD = True
except ImportError:
HAS_PYLXD = False
# PEP8
__opts__ = {}
__salt__ = {}
_pylxd_minimal_version = "2.2.5"
# Set up logging
import logging
log = logging.getLogger(__name__)
__docformat__ = 'restructuredtext en'
__virtualname__ = 'lxd'
_connection_pool = {}
# Only load in this module if the LXD configurations are in place
def __virtual__():
'''
Check for LXD configuration and if required libs are available.
'''
if get_configured_provider() is False:
return False
if get_dependencies() is False:
return False
if (LooseVersion(pylxd.__version__) <
LooseVersion(_pylxd_minimal_version)):
return False
return __virtualname__
def get_configured_provider():
'''
Return the first configured instance.
'''
return config.is_provider_configured(
__opts__,
__active_provider_name__ or __virtualname__,
('url', 'password', 'cert', 'key', 'verify_cert',)
)
def get_dependencies():
'''
Warn if dependencies aren't met.
'''
deps = {
'pylxd': HAS_PYLXD,
}
return config.check_driver_dependencies(
__virtualname__,
deps
)
#######################
# Connection Management
#######################
def _client_get(remote_addr=None, cert=None, key=None, verify_cert=True):
'''
Get an pyxld client.
remote_addr :
An URL to a remote Server, you also have to give cert and key if you
provide remote_addr and its a TCP Address!
Examples:
https://myserver.lan:8443
/var/lib/mysocket.sock
cert :
PEM Formatted SSL Certificate.
Examples:
~/.config/lxc/client.crt
key :
PEM Formatted SSL Key.
Examples:
~/.config/lxc/client.key
verify_cert : True
Wherever to verify the cert, this is by default True
but in the most cases you want to set it off as LXD
normaly uses self-signed certificates.
See the `requests-docs`_ for the SSL stuff.
.. _requests-docs: http://docs.python-requests.org/en/master/user/advanced/#ssl-cert-verification
# noqa
'''
pool_key = '|'.join((six.text_type(remote_addr),
six.text_type(cert),
six.text_type(key),
six.text_type(verify_cert),))
if pool_key in _connection_pool:
log.debug((
'Returning the client "{0}" from our connection pool'
).format(remote_addr))
return _connection_pool[pool_key]
try:
if remote_addr is None or remote_addr == '/var/lib/lxd/unix.socket':
log.debug('Trying to connect to the local unix socket')
client = pylxd.Client()
else:
if remote_addr.startswith('/'):
client = pylxd.Client(remote_addr)
else:
if cert is None or key is None:
raise SaltInvocationError(
('You have to give a Cert and '
'Key file for remote endpoints.')
)
cert = os.path.expanduser(cert)
key = os.path.expanduser(key)
if not os.path.isfile(cert):
raise SaltInvocationError(
('You have given an invalid cert path: "{0}", '
'the file does not exists or is not a file.').format(
cert
)
)
if not os.path.isfile(key):
raise SaltInvocationError(
('You have given an invalid key path: "{0}", '
'the file does not exists or is not a file.').format(
key
)
)
log.debug((
'Trying to connecto to "{0}" '
'with cert "{1}", key "{2}" and '
'verify_cert "{3!s}"'.format(
remote_addr, cert, key, verify_cert)
))
client = pylxd.Client(
endpoint=remote_addr,
cert=(cert, key,),
verify=verify_cert
)
except pylxd.exceptions.ClientConnectionFailed:
raise CommandExecutionError(
"Failed to connect to '{0}'".format(remote_addr)
)
except TypeError as e:
# Happens when the verification failed.
raise CommandExecutionError(
('Failed to connect to "{0}",'
' looks like the SSL verification failed, error was: {1}'
).format(remote_addr, six.text_type(e))
)
_connection_pool[pool_key] = client
return client
def _save_object(obj):
''' Saves an object (profile/image/container) and
translate its execpetion on failure
obj :
The object to save
'''
try:
obj.save()
except pylxd.exceptions.LXDAPIException as e:
raise CommandExecutionError(six.text_type(e))
return True
def _connect():
'''
Authenticate with LXD server and return service instance object.
'''
# Get opts
url = config.get_cloud_config_value(
'url', get_configured_provider(), __opts__, search_global=False
)
password = config.get_cloud_config_value(
'password', get_configured_provider(), __opts__, search_global=False
)
cert = config.get_cloud_config_value(
'cert', get_configured_provider(), __opts__, search_global=False
)
key = config.get_cloud_config_value(
'key', get_configured_provider(), __opts__, search_global=False
)
verify_cert = config.get_cloud_config_value(
'verify_cert', get_configured_provider(), __opts__, search_global=False
)
# Get client (connects)
client = _client_get(url, cert, key, verify_cert)
# Authenticate
if client.trusted:
return True
try:
client.authenticate(password)
except pylxd.exceptions.LXDAPIException as e:
# Wrong password
raise CommandExecutionError(six.text_type(e))
return client
########################
# lxd specific functions
########################
def test_lxd_connection(kwargs=None, call=None):
'''
Test if the connection can be made to the lxd server using
the specified credentials inside ``/etc/salt/cloud.providers``
or ``/etc/salt/cloud.providers.d/lxd.conf``
CLI Example:
.. code-block:: bash
salt-cloud -f test_lxd_connection my-lxd-config
'''
if call != 'function':
raise SaltCloudSystemExit(
'The test_lxd_connection function must be called with '
'-f or --function.'
)
try:
# Get the service instance object
_connect()
except Exception as exc:
return 'failed to connect: {0}'.format(exc)
return 'connection successful'
#######################
# salt-cloud api
#######################
def avail_images():
raise NotImplementedError
def list_nodes(conn=None, call=None):
raise NotImplementedError
def list_nodes_full(conn=None, call=None):
raise NotImplementedError
def show_instance(name, call=None):
raise NotImplementedError
def list_nodes_select(call=None):
raise NotImplementedError
def destroy(vm_, call=None):
raise NotImplementedError
def create(vm_, call=None):
raise NotImplementedError
def get_provider(name):
raise NotImplementedError
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment