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:
             "xenial/amd64"
        or an dict with type "image" with alias:
            {"type": "image",
             "alias": "xenial/amd64"}
        or image with "fingerprint":
            {"type": "image",
             "fingerprint": "SHA-256"}
        or image with "properties":
            {"type": "image",
             "properties": {
                "os": "ubuntu",
                "release": "14.04",
                "architecture": "x86_64"
             }}
        or none:
            {"type": "none"}
        or copy:
            {"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:
            [{'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
        normaly uses self-signed certificates.

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:
             "xenial/amd64"
        or an dict with type "image" with alias:
            {"type": "image",
             "alias": "xenial/amd64"}
        or image with "fingerprint":
            {"type": "image",
             "fingerprint": "SHA-256"}
        or image with "properties":
            {"type": "image",
             "properties": {
                "os": "ubuntu",
                "release": "14.04",
                "architecture": "x86_64"
             }}
        or none:
            {"type": "none"}
        or copy:
            {"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:
            [{'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
        normaly 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, six.text_type(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 "{0}"'.format(name)
            ret['changes'] = {
                'created': msg
            }
            if running is True:
                msg = msg + ' and start it.'
                ret['changes']['started'] = (
                    'Would start the container "{0}"'.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, six.text_type(e))

        msg = 'Created the container "{0}"'.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, six.text_type(e))

            msg = msg + ' and started it.'
            ret['changes'] = {
                'started': 'Started the container "{0}"'.format(name)
            }

        return _success(ret, msg)

    # Container exists, lets check for differences
    new_profiles = set(map(six.text_type, profiles))
    old_profiles = set(map(six.text_type, 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 "{0}"'.format(k))
            old_profiles.discard(k)
        else:
            profile_changes.append('Would remove profile "{0}"'.format(k))

    # Added profiles
    for k in new_profiles.difference(old_profiles):
        if not __opts__['test']:
            profile_changes.append('Added profile "{0}"'.format(k))
            old_profiles.add(k)
        else:
            profile_changes.append('Would add profile "{0}"'.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, six.text_type(e))

    if running != is_running:
        if running is True:
            if __opts__['test']:
                changes['running'] = 'Would start the container'
                return _unchanged(
                    ret,
                    ('Container "{0}" 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 "{0}" 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 "{0}"'.format(name)
            )
        else:
            container.restart(wait=True)
            changes['restarted'] = (
                'Container "{0}" has been restarted'.format(name)
            )
            return _success(
                ret,
                'Container "{0}" has been restarted'.format(name)
            )

    if not container_changed:
        return _success(ret, 'No changes')

    if __opts__['test']:
        return _unchanged(
            ret,
            'Container "{0}" would get changed.'.format(name)
        )

    return _success(ret, '{0} changes'.format(len(ret['changes'].keys())))