D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
proc
/
self
/
root
/
opt
/
saltstack
/
salt
/
lib
/
python3.10
/
site-packages
/
salt
/
states
/
Filename :
lxd_container.py
back
Copy
""" Manage LXD containers. .. versionadded:: 2019.2.0 .. note: - :ref:`pylxd` version 2 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 - you need lxd installed on the minion for the init() and version() methods. - for the config_get() and config_get() methods you need to have lxd-client installed. .. _pylxd: https://github.com/lxc/pylxd/blob/master/doc/source/installation.rst :maintainer: René Jochum <rene@jochums.at> :maturity: new :depends: python-pylxd :platform: Linux """ from salt.exceptions import CommandExecutionError, SaltInvocationError __docformat__ = "restructuredtext en" __virtualname__ = "lxd_container" # Keep in sync with: https://github.com/lxc/lxd/blob/master/shared/status.go CONTAINER_STATUS_RUNNING = 103 CONTAINER_STATUS_FROZEN = 110 CONTAINER_STATUS_STOPPED = 102 def __virtual__(): """ Only load if the lxd module is available in __salt__ """ if "lxd.version" in __salt__: return __virtualname__ return (False, "lxd module could not be loaded") def present( name, running=None, source=None, profiles=None, config=None, devices=None, architecture="x86_64", ephemeral=False, restart_on_change=False, remote_addr=None, cert=None, key=None, verify_cert=True, ): """ Create the named container if it does not exist name The name of the container to be created running : None * If ``True``, ensure that the container is running * If ``False``, ensure that the container is stopped * If ``None``, do nothing with regards to the running state of the container source : None Can be either a string containing an image alias: .. code-block:: none "xenial/amd64" or an dict with type "image" with alias: .. code-block:: python {"type": "image", "alias": "xenial/amd64"} or image with "fingerprint": .. code-block:: python {"type": "image", "fingerprint": "SHA-256"} or image with "properties": .. code-block:: python {"type": "image", "properties": { "os": "ubuntu", "release": "14.04", "architecture": "x86_64" }} or none: .. code-block:: python {"type": "none"} or copy: .. code-block:: python {"type": "copy", "source": "my-old-container"} profiles : ['default'] List of profiles to apply on this container config : A config dict or None (None = unset). Can also be a list: .. code-block:: python [{'key': 'boot.autostart', 'value': 1}, {'key': 'security.privileged', 'value': '1'}] devices : A device dict or None (None = unset). architecture : 'x86_64' Can be one of the following: * unknown * i686 * x86_64 * armv7l * aarch64 * ppc * ppc64 * ppc64le * s390x ephemeral : False Destroy this container after stop? restart_on_change : False Restart the container when we detect changes on the config or its devices? remote_addr : An URL to a remote Server, you also have to give cert and key if you provide remote_addr! Examples: https://myserver.lan:8443 /var/lib/mysocket.sock cert : PEM Formatted SSL Zertifikate. 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 normally uses self-signed certificates. """ if profiles is None: profiles = ["default"] if source is None: source = {} ret = { "name": name, "running": running, "profiles": profiles, "source": source, "config": config, "devices": devices, "architecture": architecture, "ephemeral": ephemeral, "restart_on_change": restart_on_change, "remote_addr": remote_addr, "cert": cert, "key": key, "verify_cert": verify_cert, "changes": {}, } container = None try: container = __salt__["lxd.container_get"]( name, remote_addr, cert, key, verify_cert, _raw=True ) except CommandExecutionError as e: return _error(ret, str(e)) except SaltInvocationError as e: # Profile not found pass if container is None: if __opts__["test"]: # Test is on, just return that we would create the container msg = 'Would create the container "{}"'.format(name) ret["changes"] = {"created": msg} if running is True: msg = msg + " and start it." ret["changes"]["started"] = 'Would start the container "{}"'.format( name ) ret["changes"] = {"created": msg} return _unchanged(ret, msg) # create the container try: __salt__["lxd.container_create"]( name, source, profiles, config, devices, architecture, ephemeral, True, # Wait remote_addr, cert, key, verify_cert, ) except CommandExecutionError as e: return _error(ret, str(e)) msg = 'Created the container "{}"'.format(name) ret["changes"] = {"created": msg} if running is True: try: __salt__["lxd.container_start"]( name, remote_addr, cert, key, verify_cert ) except CommandExecutionError as e: return _error(ret, str(e)) msg = msg + " and started it." ret["changes"] = {"started": 'Started the container "{}"'.format(name)} return _success(ret, msg) # Container exists, lets check for differences new_profiles = set(map(str, profiles)) old_profiles = set(map(str, container.profiles)) container_changed = False profile_changes = [] # Removed profiles for k in old_profiles.difference(new_profiles): if not __opts__["test"]: profile_changes.append('Removed profile "{}"'.format(k)) old_profiles.discard(k) else: profile_changes.append('Would remove profile "{}"'.format(k)) # Added profiles for k in new_profiles.difference(old_profiles): if not __opts__["test"]: profile_changes.append('Added profile "{}"'.format(k)) old_profiles.add(k) else: profile_changes.append('Would add profile "{}"'.format(k)) if profile_changes: container_changed = True ret["changes"]["profiles"] = profile_changes container.profiles = list(old_profiles) # Config and devices changes config, devices = __salt__["lxd.normalize_input_values"](config, devices) changes = __salt__["lxd.sync_config_devices"]( container, config, devices, __opts__["test"] ) if changes: container_changed = True ret["changes"].update(changes) is_running = container.status_code == CONTAINER_STATUS_RUNNING if not __opts__["test"]: try: __salt__["lxd.pylxd_save_object"](container) except CommandExecutionError as e: return _error(ret, str(e)) if running != is_running: if running is True: if __opts__["test"]: changes["running"] = "Would start the container" return _unchanged( ret, 'Container "{}" would get changed and started.'.format(name), ) else: container.start(wait=True) changes["running"] = "Started the container" elif running is False: if __opts__["test"]: changes["stopped"] = "Would stopped the container" return _unchanged( ret, 'Container "{}" would get changed and stopped.'.format(name), ) else: container.stop(wait=True) changes["stopped"] = "Stopped the container" if ( (running is True or running is None) and is_running and restart_on_change and container_changed ): if __opts__["test"]: changes["restarted"] = "Would restart the container" return _unchanged(ret, 'Would restart the container "{}"'.format(name)) else: container.restart(wait=True) changes["restarted"] = 'Container "{}" has been restarted'.format(name) return _success(ret, 'Container "{}" has been restarted'.format(name)) if not container_changed: return _success(ret, "No changes") if __opts__["test"]: return _unchanged(ret, 'Container "{}" would get changed.'.format(name)) return _success(ret, "{} changes".format(len(ret["changes"].keys()))) def absent(name, stop=False, remote_addr=None, cert=None, key=None, verify_cert=True): """ Ensure a LXD container is not present, destroying it if present name : The name of the container to destroy stop : stop before destroying default: false remote_addr : An URL to a remote Server, you also have to give cert and key if you provide remote_addr! Examples: https://myserver.lan:8443 /var/lib/mysocket.sock cert : PEM Formatted SSL Zertifikate. 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 normally uses self-signed certificates. """ ret = { "name": name, "stop": stop, "remote_addr": remote_addr, "cert": cert, "key": key, "verify_cert": verify_cert, "changes": {}, } try: container = __salt__["lxd.container_get"]( name, remote_addr, cert, key, verify_cert, _raw=True ) except CommandExecutionError as e: return _error(ret, str(e)) except SaltInvocationError as e: # Container not found return _success(ret, 'Container "{}" not found.'.format(name)) if __opts__["test"]: ret["changes"] = {"removed": 'Container "{}" would get deleted.'.format(name)} return _unchanged(ret, ret["changes"]["removed"]) if stop and container.status_code == CONTAINER_STATUS_RUNNING: container.stop(wait=True) container.delete(wait=True) ret["changes"]["deleted"] = 'Container "{}" has been deleted.'.format(name) return _success(ret, ret["changes"]["deleted"]) def running( name, restart=False, remote_addr=None, cert=None, key=None, verify_cert=True ): """ Ensure a LXD container is running and restart it if restart is True name : The name of the container to start/restart. restart : restart the container if it is already started. remote_addr : An URL to a remote Server, you also have to give cert and key if you provide remote_addr! Examples: https://myserver.lan:8443 /var/lib/mysocket.sock cert : PEM Formatted SSL Zertifikate. 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 normally uses self-signed certificates. """ ret = { "name": name, "restart": restart, "remote_addr": remote_addr, "cert": cert, "key": key, "verify_cert": verify_cert, "changes": {}, } try: container = __salt__["lxd.container_get"]( name, remote_addr, cert, key, verify_cert, _raw=True ) except CommandExecutionError as e: return _error(ret, str(e)) except SaltInvocationError as e: # Container not found return _error(ret, 'Container "{}" not found'.format(name)) is_running = container.status_code == CONTAINER_STATUS_RUNNING if is_running: if not restart: return _success(ret, 'The container "{}" is already running'.format(name)) else: if __opts__["test"]: ret["changes"]["restarted"] = 'Would restart the container "{}"'.format( name ) return _unchanged(ret, ret["changes"]["restarted"]) else: container.restart(wait=True) ret["changes"]["restarted"] = 'Restarted the container "{}"'.format( name ) return _success(ret, ret["changes"]["restarted"]) if __opts__["test"]: ret["changes"]["started"] = 'Would start the container "{}"'.format(name) return _unchanged(ret, ret["changes"]["started"]) container.start(wait=True) ret["changes"]["started"] = 'Started the container "{}"'.format(name) return _success(ret, ret["changes"]["started"]) def frozen(name, start=True, remote_addr=None, cert=None, key=None, verify_cert=True): """ Ensure a LXD container is frozen, start and freeze it if start is true name : The name of the container to freeze start : start and freeze it remote_addr : An URL to a remote Server, you also have to give cert and key if you provide remote_addr! Examples: https://myserver.lan:8443 /var/lib/mysocket.sock cert : PEM Formatted SSL Zertifikate. 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 normally uses self-signed certificates. """ ret = { "name": name, "start": start, "remote_addr": remote_addr, "cert": cert, "key": key, "verify_cert": verify_cert, "changes": {}, } try: container = __salt__["lxd.container_get"]( name, remote_addr, cert, key, verify_cert, _raw=True ) except CommandExecutionError as e: return _error(ret, str(e)) except SaltInvocationError as e: # Container not found return _error(ret, 'Container "{}" not found'.format(name)) if container.status_code == CONTAINER_STATUS_FROZEN: return _success(ret, 'Container "{}" is alredy frozen'.format(name)) is_running = container.status_code == CONTAINER_STATUS_RUNNING if not is_running and not start: return _error( ret, 'Container "{}" is not running and start is False, cannot freeze it'.format( name ), ) elif not is_running and start: if __opts__["test"]: ret["changes"][ "started" ] = 'Would start the container "{}" and freeze it after'.format(name) return _unchanged(ret, ret["changes"]["started"]) else: container.start(wait=True) ret["changes"]["started"] = 'Start the container "{}"'.format(name) if __opts__["test"]: ret["changes"]["frozen"] = 'Would freeze the container "{}"'.format(name) return _unchanged(ret, ret["changes"]["frozen"]) container.freeze(wait=True) ret["changes"]["frozen"] = 'Froze the container "{}"'.format(name) return _success(ret, ret["changes"]["frozen"]) def stopped(name, kill=False, remote_addr=None, cert=None, key=None, verify_cert=True): """ Ensure a LXD container is stopped, kill it if kill is true else stop it name : The name of the container to stop kill : kill if true remote_addr : An URL to a remote Server, you also have to give cert and key if you provide remote_addr! Examples: https://myserver.lan:8443 /var/lib/mysocket.sock cert : PEM Formatted SSL Zertifikate. 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 normally uses self-signed certificates. """ ret = { "name": name, "kill": kill, "remote_addr": remote_addr, "cert": cert, "key": key, "verify_cert": verify_cert, "changes": {}, } try: container = __salt__["lxd.container_get"]( name, remote_addr, cert, key, verify_cert, _raw=True ) except CommandExecutionError as e: return _error(ret, str(e)) except SaltInvocationError as e: # Container not found return _error(ret, 'Container "{}" not found'.format(name)) if container.status_code == CONTAINER_STATUS_STOPPED: return _success(ret, 'Container "{}" is already stopped'.format(name)) if __opts__["test"]: ret["changes"]["stopped"] = 'Would stop the container "{}"'.format(name) return _unchanged(ret, ret["changes"]["stopped"]) container.stop(force=kill, wait=True) ret["changes"]["stopped"] = 'Stopped the container "{}"'.format(name) return _success(ret, ret["changes"]["stopped"]) def migrated( name, remote_addr, cert, key, verify_cert, src_remote_addr, stop_and_start=False, src_cert=None, src_key=None, src_verify_cert=None, ): """Ensure a container is migrated to another host If the container is running, it either must be shut down first (use stop_and_start=True) or criu must be installed on the source and destination machines. For this operation both certs need to be authenticated, use :mod:`lxd.authenticate <salt.states.lxd.authenticate` to authenticate your cert(s). name : The container to migrate remote_addr : An URL to the destination remote Server Examples: https://myserver.lan:8443 /var/lib/mysocket.sock cert : PEM Formatted SSL Zertifikate. 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 normally uses self-signed certificates. src_remote_addr : An URL to the source remote Server Examples: https://myserver.lan:8443 /var/lib/mysocket.sock stop_and_start: Stop before migrating and start after src_cert : PEM Formatted SSL Zertifikate, if None we copy "cert" Examples: ~/.config/lxc/client.crt src_key : PEM Formatted SSL Key, if None we copy "key" Examples: ~/.config/lxc/client.key src_verify_cert : Wherever to verify the cert, if None we copy "verify_cert" """ ret = { "name": name, "remote_addr": remote_addr, "cert": cert, "key": key, "verify_cert": verify_cert, "src_remote_addr": src_remote_addr, "src_and_start": stop_and_start, "src_cert": src_cert, "src_key": src_key, "changes": {}, } dest_container = None try: dest_container = __salt__["lxd.container_get"]( name, remote_addr, cert, key, verify_cert, _raw=True ) except CommandExecutionError as e: return _error(ret, str(e)) except SaltInvocationError as e: # Destination container not found pass if dest_container is not None: return _success(ret, 'Container "{}" exists on the destination'.format(name)) if src_verify_cert is None: src_verify_cert = verify_cert try: __salt__["lxd.container_get"]( name, src_remote_addr, src_cert, src_key, src_verify_cert, _raw=True ) except CommandExecutionError as e: return _error(ret, str(e)) except SaltInvocationError as e: # Container not found return _error(ret, 'Source Container "{}" not found'.format(name)) if __opts__["test"]: ret["changes"][ "migrated" ] = 'Would migrate the container "{}" from "{}" to "{}"'.format( name, src_remote_addr, remote_addr ) return _unchanged(ret, ret["changes"]["migrated"]) try: __salt__["lxd.container_migrate"]( name, stop_and_start, remote_addr, cert, key, verify_cert, src_remote_addr, src_cert, src_key, src_verify_cert, ) except CommandExecutionError as e: return _error(ret, str(e)) ret["changes"]["migrated"] = 'Migrated the container "{}" from "{}" to "{}"'.format( name, src_remote_addr, remote_addr ) return _success(ret, ret["changes"]["migrated"]) def _success(ret, success_msg): ret["result"] = True ret["comment"] = success_msg if "changes" not in ret: ret["changes"] = {} return ret def _unchanged(ret, msg): ret["result"] = None ret["comment"] = msg if "changes" not in ret: ret["changes"] = {} return ret def _error(ret, err_msg): ret["result"] = False ret["comment"] = err_msg if "changes" not in ret: ret["changes"] = {} return ret