Last active
February 27, 2018 20:49
-
-
Save jochumdev/e8ba889179509a6c79f3d6103322f1a9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- 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 <[email protected]> | |
: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