PK œqhYî¶J‚ßF ßF ) nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/
Dir : /proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/states/ |
Server: Linux ngx353.inmotionhosting.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 IP: 209.182.202.254 |
Dir : //proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/states/docker_network.py |
""" Management of Docker networks .. versionadded:: 2017.7.0 :depends: docker_ Python module .. note:: Older releases of the Python bindings for Docker were called docker-py_ in PyPI. All releases of docker_, and releases of docker-py_ >= 1.6.0 are supported. These python bindings can easily be installed using :py:func:`pip.install <salt.modules.pip.install>`: .. code-block:: bash salt myminion pip.install docker To upgrade from docker-py_ to docker_, you must first uninstall docker-py_, and then install docker_: .. code-block:: bash salt myminion pip.uninstall docker-py salt myminion pip.install docker .. _docker: https://pypi.python.org/pypi/docker .. _docker-py: https://pypi.python.org/pypi/docker-py These states were moved from the :mod:`docker <salt.states.docker>` state module (formerly called **dockerng**) in the 2017.7.0 release. """ import copy import logging import random import string import salt.utils.dockermod.translate.network from salt._compat import ipaddress from salt.exceptions import CommandExecutionError log = logging.getLogger(__name__) # Define the module's virtual name __virtualname__ = "docker_network" __virtual_aliases__ = ("moby_network",) __deprecated__ = ( 3009, "docker", "https://github.com/saltstack/saltext-docker", ) def __virtual__(): """ Only load if the docker execution module is available """ if "docker.version" in __salt__: return __virtualname__ return (False, __salt__.missing_fun_string("docker.version")) def _normalize_pools(existing, desired): pools = {"existing": {4: None, 6: None}, "desired": {4: None, 6: None}} for pool in existing["Config"]: subnet = ipaddress.ip_network(pool.get("Subnet")) pools["existing"][subnet.version] = pool for pool in desired["Config"]: subnet = ipaddress.ip_network(pool.get("Subnet")) if pools["desired"][subnet.version] is not None: raise ValueError(f"Only one IPv{subnet.version} pool is permitted") else: pools["desired"][subnet.version] = pool if pools["desired"][6] and not pools["desired"][4]: raise ValueError( "An IPv4 pool is required when an IPv6 pool is used. See the " "documentation for details." ) # The pools will be sorted when comparing existing["Config"] = [ pools["existing"][x] for x in (4, 6) if pools["existing"][x] is not None ] desired["Config"] = [ pools["desired"][x] for x in (4, 6) if pools["desired"][x] is not None ] def present( name, skip_translate=None, ignore_collisions=False, validate_ip_addrs=True, containers=None, reconnect=True, **kwargs, ): """ .. versionchanged:: 2018.3.0 Support added for network configuration options other than ``driver`` and ``driver_opts``, as well as IPAM configuration. Ensure that a network is present .. note:: This state supports all arguments for network and IPAM pool configuration which are available for the release of docker-py installed on the minion. For that reason, the arguments described below in the :ref:`NETWORK CONFIGURATION <salt-states-docker-network-present-netconf>` and :ref:`IP ADDRESS MANAGEMENT (IPAM) <salt-states-docker-network-present-ipam>` sections may not accurately reflect what is available on the minion. The :py:func:`docker.get_client_args <salt.modules.dockermod.get_client_args>` function can be used to check the available arguments for the installed version of docker-py (they are found in the ``network_config`` and ``ipam_config`` sections of the return data), but Salt will not prevent a user from attempting to use an argument which is unsupported in the release of Docker which is installed. In those cases, network creation be attempted but will fail. name Network name skip_translate This function translates Salt SLS input into the format which docker-py expects. However, in the event that Salt's translation logic fails (due to potential changes in the Docker Remote API, or to bugs in the translation code), this argument can be used to exert granular control over which arguments are translated and which are not. Pass this argument as a comma-separated list (or Python list) of arguments, and translation for each passed argument name will be skipped. Alternatively, pass ``True`` and *all* translation will be skipped. Skipping tranlsation allows for arguments to be formatted directly in the format which docker-py expects. This allows for API changes and other issues to be more easily worked around. See the following links for more information: - `docker-py Low-level API`_ - `Docker Engine API`_ .. versionadded:: 2018.3.0 .. _`docker-py Low-level API`: http://docker-py.readthedocs.io/en/stable/api.html#docker.api.container.ContainerApiMixin.create_container .. _`Docker Engine API`: https://docs.docker.com/engine/api/v1.33/#operation/ContainerCreate ignore_collisions : False Since many of docker-py's arguments differ in name from their CLI counterparts (with which most Docker users are more familiar), Salt detects usage of these and aliases them to the docker-py version of that argument. However, if both the alias and the docker-py version of the same argument (e.g. ``options`` and ``driver_opts``) are used, an error will be raised. Set this argument to ``True`` to suppress these errors and keep the docker-py version of the argument. .. versionadded:: 2018.3.0 validate_ip_addrs : True For parameters which accept IP addresses/subnets as input, validation will be performed. To disable, set this to ``False``. .. versionadded:: 2018.3.0 containers A list of containers which should be connected to this network. .. note:: As of the 2018.3.0 release, this is not the recommended way of managing a container's membership in a network, for a couple reasons: 1. It does not support setting static IPs, aliases, or links in the container's IP configuration. 2. If a :py:func:`docker_container.running <salt.states.docker_container.running>` state replaces a container, it will not be reconnected to the network until the ``docker_network.present`` state is run again. Since containers often have ``require`` requisites to ensure that the network is present, this means that the ``docker_network.present`` state ends up being run *before* the :py:func:`docker_container.running <salt.states.docker_container.running>`, leaving the container unattached at the end of the Salt run. For these reasons, it is recommended to use :ref:`docker_container.running's network management support <salt-states-docker-container-network-management>`. reconnect : True If ``containers`` is not used, and the network is replaced, then Salt will keep track of the containers which were connected to the network and reconnect them to the network after it is replaced. Salt will first attempt to reconnect using the same IP the container had before the network was replaced. If that fails (for instance, if the network was replaced because the subnet was modified), then the container will be reconnected without an explicit IP address, and its IP will be assigned by Docker. Set this option to ``False`` to keep Salt from trying to reconnect containers. This can be useful in some cases when :ref:`managing static IPs in docker_container.running <salt-states-docker-container-network-management>`. For instance, if a network's subnet is modified, it is likely that the static IP will need to be updated in the ``docker_container.running`` state as well. When the network is replaced, the initial reconnect attempt would fail, and the container would be reconnected with an automatically-assigned IP address. Then, when the ``docker_container.running`` state executes, it would disconnect the network *again* and reconnect using the new static IP. Disabling the reconnect behavior in these cases would prevent the unnecessary extra reconnection. .. versionadded:: 2018.3.0 .. _salt-states-docker-network-present-netconf: **NETWORK CONFIGURATION ARGUMENTS** driver Network driver .. code-block:: yaml mynet: docker_network.present: - driver: macvlan driver_opts (or *driver_opt*, or *options*) Options for the network driver. Either a dictionary of option names and values or a Python list of strings in the format ``varname=value``. The below three examples are equivalent: .. code-block:: yaml mynet: docker_network.present: - driver: macvlan - driver_opts: macvlan_mode=bridge,parent=eth0 .. code-block:: yaml mynet: docker_network.present: - driver: macvlan - driver_opts: - macvlan_mode=bridge - parent=eth0 .. code-block:: yaml mynet: docker_network.present: - driver: macvlan - driver_opts: - macvlan_mode: bridge - parent: eth0 The options can also simply be passed as a dictionary, though this can be error-prone due to some :ref:`idiosyncrasies <yaml-idiosyncrasies>` with how PyYAML loads nested data structures: .. code-block:: yaml mynet: docker_network.present: - driver: macvlan - driver_opts: macvlan_mode: bridge parent: eth0 check_duplicate : True If ``True``, checks for networks with duplicate names. Since networks are primarily keyed based on a random ID and not on the name, and network name is strictly a user-friendly alias to the network which is uniquely identified using ID, there is no guaranteed way to check for duplicates. This option providess a best effort, checking for any networks which have the same name, but it is not guaranteed to catch all name collisions. .. code-block:: yaml mynet: docker_network.present: - check_duplicate: False internal : False If ``True``, restricts external access to the network .. code-block:: yaml mynet: docker_network.present: - internal: True labels Add metadata to the network. Labels can be set both with and without values, and labels with values can be passed either as ``key=value`` or ``key: value`` pairs. For example, while the below would be very confusing to read, it is technically valid, and demonstrates the different ways in which labels can be passed: .. code-block:: yaml mynet: docker_network.present: - labels: - foo - bar=baz - hello: world The labels can also simply be passed as a YAML dictionary, though this can be error-prone due to some :ref:`idiosyncrasies <yaml-idiosyncrasies>` with how PyYAML loads nested data structures: .. code-block:: yaml foo: docker_network.present: - labels: foo: '' bar: baz hello: world .. versionchanged:: 2018.3.0 Methods for specifying labels can now be mixed. Earlier releases required either labels with or without values. enable_ipv6 (or *ipv6*) : False Enable IPv6 on the network .. code-block:: yaml mynet: docker_network.present: - enable_ipv6: True .. note:: While it should go without saying, this argument must be set to ``True`` to :ref:`configure an IPv6 subnet <salt-states-docker-network-present-ipam>`. Also, if this option is turned on without an IPv6 subnet explicitly configured, you will get an error unless you have set up a fixed IPv6 subnet. Consult the `Docker IPv6 docs`_ for information on how to do this. .. _`Docker IPv6 docs`: https://docs.docker.com/v17.09/engine/userguide/networking/default_network/ipv6/ attachable : False If ``True``, and the network is in the global scope, non-service containers on worker nodes will be able to connect to the network. .. code-block:: yaml mynet: docker_network.present: - attachable: True .. note:: This option cannot be reliably managed on CentOS 7. This is because while support for this option was added in API version 1.24, its value was not added to the inpsect results until API version 1.26. The version of Docker which is available for CentOS 7 runs API version 1.24, meaning that while Salt can pass this argument to the API, it has no way of knowing the value of this config option in an existing Docker network. scope Specify the network's scope (``local``, ``global`` or ``swarm``) .. code-block:: yaml mynet: docker_network.present: - scope: local ingress : False If ``True``, create an ingress network which provides the routing-mesh in swarm mode .. code-block:: yaml mynet: docker_network.present: - ingress: True .. _salt-states-docker-network-present-ipam: **IP ADDRESS MANAGEMENT (IPAM)** This state supports networks with either IPv4, or both IPv4 and IPv6. If configuring IPv4, then you can pass the :ref:`IPAM pool arguments <salt-states-docker-network-present-ipam-pool-arguments>` below as individual arguments. However, if configuring IPv4 and IPv6, the arguments must be passed as a list of dictionaries, in the ``ipam_pools`` argument (click :ref:`here <salt-states-docker-network-present-ipam-examples>` for some examples). `These docs`_ also have more information on these arguments. .. _`These docs`: http://docker-py.readthedocs.io/en/stable/api.html#docker.types.IPAMPool *IPAM ARGUMENTS* ipam_driver IPAM driver to use, if different from the default one .. code-block:: yaml mynet: docker_network.present: - ipam_driver: foo ipam_opts Options for the IPAM driver. Either a dictionary of option names and values or a Python list of strings in the format ``varname=value``. The below three examples are equivalent: .. code-block:: yaml mynet: docker_network.present: - ipam_driver: foo - ipam_opts: foo=bar,baz=qux .. code-block:: yaml mynet: docker_network.present: - ipam_driver: foo - ipam_opts: - foo=bar - baz=qux .. code-block:: yaml mynet: docker_network.present: - ipam_driver: foo - ipam_opts: - foo: bar - baz: qux The options can also simply be passed as a dictionary, though this can be error-prone due to some :ref:`idiosyncrasies <yaml-idiosyncrasies>` with how PyYAML loads nested data structures: .. code-block:: yaml mynet: docker_network.present: - ipam_driver: macvlan - ipam_opts: foo: bar baz: qux .. _salt-states-docker-network-present-ipam-pool-arguments: *IPAM POOL ARGUMENTS* subnet Subnet in CIDR format that represents a network segment iprange (or *ip_range*) Allocate container IP from a sub-range within the subnet Subnet in CIDR format that represents a network segment gateway IPv4 or IPv6 gateway for the master subnet aux_addresses (or *aux_address*) A dictionary of mapping container names to IP addresses which should be allocated for them should they connect to the network. Either a dictionary of option names and values or a Python list of strings in the format ``host=ipaddr``. .. _salt-states-docker-network-present-ipam-examples: *IPAM CONFIGURATION EXAMPLES* Below is an example of an IPv4-only network (keep in mind that ``subnet`` is the only required argument). .. code-block:: yaml mynet: docker_network.present: - subnet: 10.0.20.0/24 - iprange: 10.0.20.128/25 - gateway: 10.0.20.254 - aux_addresses: - foo.bar.tld: 10.0.20.50 - hello.world.tld: 10.0.20.51 .. note:: The ``aux_addresses`` can be passed differently, in the same way that ``driver_opts`` and ``ipam_opts`` can. This same network could also be configured this way: .. code-block:: yaml mynet: docker_network.present: - ipam_pools: - subnet: 10.0.20.0/24 iprange: 10.0.20.128/25 gateway: 10.0.20.254 aux_addresses: foo.bar.tld: 10.0.20.50 hello.world.tld: 10.0.20.51 Here is an example of a mixed IPv4/IPv6 subnet. .. code-block:: yaml mynet: docker_network.present: - ipam_pools: - subnet: 10.0.20.0/24 gateway: 10.0.20.1 - subnet: fe3f:2180:26:1::/123 gateway: fe3f:2180:26:1::1 """ ret = {"name": name, "changes": {}, "result": False, "comment": ""} try: network = __salt__["docker.inspect_network"](name) except CommandExecutionError as exc: msg = str(exc) if "404" in msg: # Network not present network = None else: ret["comment"] = msg return ret # map container's IDs to names to_connect = {} missing_containers = [] stopped_containers = [] for cname in __utils__["args.split_input"](containers or []): try: cinfo = __salt__["docker.inspect_container"](cname) except CommandExecutionError: missing_containers.append(cname) else: try: cid = cinfo["Id"] except KeyError: missing_containers.append(cname) else: if not cinfo.get("State", {}).get("Running", False): stopped_containers.append(cname) else: to_connect[cid] = {"Name": cname} if missing_containers: ret.setdefault("warnings", []).append( "The following containers do not exist: {}.".format( ", ".join(missing_containers) ) ) if stopped_containers: ret.setdefault("warnings", []).append( "The following containers are not running: {}.".format( ", ".join(stopped_containers) ) ) # We might disconnect containers in the process of recreating the network, # we'll need to keep track these containers so we can reconnect them later. disconnected_containers = {} try: kwargs = __utils__["docker.translate_input"]( salt.utils.dockermod.translate.network, skip_translate=skip_translate, ignore_collisions=ignore_collisions, validate_ip_addrs=validate_ip_addrs, **__utils__["args.clean_kwargs"](**kwargs), ) except Exception as exc: # pylint: disable=broad-except ret["comment"] = str(exc) return ret # Separate out the IPAM config options and build the IPAM config dict ipam_kwargs = {} ipam_kwarg_names = ["ipam", "ipam_driver", "ipam_opts", "ipam_pools"] ipam_kwarg_names.extend( __salt__["docker.get_client_args"]("ipam_config")["ipam_config"] ) for key in ipam_kwarg_names: try: ipam_kwargs[key] = kwargs.pop(key) except KeyError: pass if "ipam" in ipam_kwargs: if len(ipam_kwargs) > 1: ret["comment"] = ( "Cannot mix the 'ipam' argument with any of the IPAM config " "arguments. See documentation for details." ) return ret ipam_config = ipam_kwargs["ipam"] else: ipam_pools = ipam_kwargs.pop("ipam_pools", ()) try: ipam_config = __utils__["docker.create_ipam_config"]( *ipam_pools, **ipam_kwargs ) except Exception as exc: # pylint: disable=broad-except ret["comment"] = str(exc) return ret # We'll turn this off if we decide below that creating the network is not # necessary. create_network = True if network is not None: log.debug("Docker network '%s' already exists", name) # Set the comment now to say that it already exists, if we need to # recreate the network with new config we'll update the comment later. ret["comment"] = ( f"Network '{name}' already exists, and is configured as specified" ) log.trace("Details of docker network '%s': %s", name, network) temp_net_name = "".join( random.choice(string.ascii_lowercase) for _ in range(20) ) try: # When using enable_ipv6, you *must* provide a subnet. But we don't # care about the subnet when we make our temp network, we only care # about the non-IPAM values in the network. And we also do not want # to try some hacky workaround where we choose a small IPv6 subnet # to pass when creating the temp network, that may end up # overlapping with a large IPv6 subnet already in use by Docker. # So, for purposes of comparison we will create the temp network # with enable_ipv6=False and then munge the inspect results before # performing the comparison. Note that technically it is not # required that one specify both v4 and v6 subnets when creating a # network, but not specifying IPv4 makes it impossible for us to # reliably compare the SLS input to the existing network, as we # wouldng't know if the IPv4 subnet in the existing network was # explicitly configured or was automatically assigned by Docker. enable_ipv6 = kwargs.pop("enable_ipv6", None) kwargs_tmp = kwargs driver = kwargs.get( "driver", ) driver_opts = kwargs.get("options", {}) bridge_name = driver_opts.get("com.docker.network.bridge.name", None) if driver == "bridge" and bridge_name is not None: tmp_name = str(bridge_name) + "comp" kwargs_tmp["options"]["com.docker.network.bridge.name"] = tmp_name[-14:] __salt__["docker.create_network"]( temp_net_name, skip_translate=True, # No need to translate (already did) enable_ipv6=False, **kwargs_tmp, ) except CommandExecutionError as exc: ret["comment"] = "Failed to create temp network for comparison: {}".format( str(exc) ) return ret else: # Replace the value so we can use it later if enable_ipv6 is not None: kwargs["enable_ipv6"] = enable_ipv6 try: try: temp_net_info = __salt__["docker.inspect_network"](temp_net_name) except CommandExecutionError as exc: ret["comment"] = f"Failed to inspect temp network: {str(exc)}" return ret else: temp_net_info["EnableIPv6"] = bool(enable_ipv6) # Replace the IPAM configuration in the temp network with the IPAM # config dict we created earlier, for comparison purposes. This is # necessary because we cannot create two networks that have # overlapping subnets (the Docker Engine will throw an error). temp_net_info["IPAM"] = ipam_config existing_pool_count = len(network["IPAM"]["Config"]) desired_pool_count = len(temp_net_info["IPAM"]["Config"]) def is_default_pool(x): return True if sorted(x) == ["Gateway", "Subnet"] else False if ( desired_pool_count == 0 and existing_pool_count == 1 and is_default_pool(network["IPAM"]["Config"][0]) ): # If we're not explicitly configuring an IPAM pool, then we # don't care what the subnet is. Docker networks created with # no explicit IPAM configuration are assigned a single IPAM # pool containing just a subnet and gateway. If the above if # statement resolves as True, then we know that both A) we # aren't explicitly configuring IPAM, and B) the existing # network appears to be one that was created without an # explicit IPAM configuration (since it has the default pool # config values). Of course, it could be possible that the # existing network was created with a single custom IPAM pool, # with just a subnet and gateway. But even if this was the # case, the fact that we aren't explicitly enforcing IPAM # configuration means we don't really care what the existing # IPAM configuration is. At any rate, to avoid IPAM differences # when comparing the existing network to the temp network, we # need to clear the existing network's IPAM configuration. network["IPAM"]["Config"] = [] changes = __salt__["docker.compare_networks"]( network, temp_net_info, ignore="Name,Id,Created,Containers" ) if not changes: # No changes to the network, so we'll be keeping the existing # network and at most just connecting containers to it. create_network = False else: ret["changes"][name] = changes if __opts__["test"]: ret["result"] = None ret["comment"] = "Network would be recreated with new config" return ret if network["Containers"]: # We've removed the network, so there are now no containers # attached to it. However, once we recreate the network # with the new configuration we may need to reconnect the # containers that were previously connected. Even if we're # not reconnecting, we still need to track the containers # so that we can report on which were disconnected. disconnected_containers = copy.deepcopy(network["Containers"]) if not containers and reconnect: # Grab the links and aliases from each connected # container so that we have them when we attempt to # reconnect later for cid in disconnected_containers: try: cinfo = __salt__["docker.inspect_container"](cid) netinfo = cinfo["NetworkSettings"]["Networks"][name] # Links and Aliases will be None if not # explicitly set, hence using "or" instead of # placing the empty list inside the dict.get net_links = netinfo.get("Links") or [] net_aliases = netinfo.get("Aliases") or [] if net_links: disconnected_containers[cid]["Links"] = net_links if net_aliases: disconnected_containers[cid][ "Aliases" ] = net_aliases except (CommandExecutionError, KeyError, ValueError): continue remove_result = _remove_network(network) if not remove_result["result"]: return remove_result # Replace the Containers key with an empty dict so that when we # check for connnected containers below, we correctly see that # there are none connected. network["Containers"] = {} finally: try: __salt__["docker.remove_network"](temp_net_name) except CommandExecutionError as exc: ret.setdefault("warnings", []).append( f"Failed to remove temp network '{temp_net_name}': {exc}." ) if create_network: log.debug("Network '%s' will be created", name) if __opts__["test"]: # NOTE: if the container already existed and needed to be # recreated, and we were in test mode, we would have already exited # above with a comment about the network needing to be recreated. # So, even though the below block to create the network would be # executed to create the network both when it's being recreated and # when it's being created for the first time, the below comment is # still accurate. ret["result"] = None ret["comment"] = "Network will be created" return ret kwargs["ipam"] = ipam_config try: __salt__["docker.create_network"]( name, skip_translate=True, # No need to translate (already did) **kwargs, ) except Exception as exc: # pylint: disable=broad-except ret["comment"] = f"Failed to create network '{name}': {exc}" return ret else: action = "recreated" if network is not None else "created" ret["changes"][action] = True ret["comment"] = "Network '{}' {}".format( name, "created" if network is None else "was replaced with updated config", ) # Make sure the "Containers" key exists for logic below network = {"Containers": {}} # If no containers were specified in the state but we have disconnected # some in the process of recreating the network, we should reconnect those # containers. if containers is None and reconnect and disconnected_containers: to_connect = disconnected_containers # Don't try to connect any containers which are already connected. If we # created/re-created the network, then network['Containers'] will be empty # and no containers will be deleted from the to_connect dict (the result # being that we will reconnect all containers in the to_connect dict). # list() is used here because we will potentially be modifying the # dictionary during iteration. for cid in list(to_connect): if cid in network["Containers"]: del to_connect[cid] errors = [] if to_connect: for cid, connect_info in to_connect.items(): connect_kwargs = {} if cid in disconnected_containers: for key_name, arg_name in ( ("IPv4Address", "ipv4_address"), ("IPV6Address", "ipv6_address"), ("Links", "links"), ("Aliases", "aliases"), ): try: connect_kwargs[arg_name] = connect_info[key_name] except (KeyError, AttributeError): continue else: if key_name.endswith("Address"): connect_kwargs[arg_name] = connect_kwargs[arg_name].rsplit( "/", 1 )[0] try: __salt__["docker.connect_container_to_network"]( cid, name, **connect_kwargs ) except CommandExecutionError as exc: if not connect_kwargs: errors.append(str(exc)) else: # We failed to reconnect with the container's old IP # configuration. Reconnect using automatic IP config. try: __salt__["docker.connect_container_to_network"](cid, name) except CommandExecutionError as exc: errors.append(str(exc)) else: ret["changes"].setdefault( ( "reconnected" if cid in disconnected_containers else "connected" ), [], ).append(connect_info["Name"]) else: ret["changes"].setdefault( "reconnected" if cid in disconnected_containers else "connected", [] ).append(connect_info["Name"]) if errors: if ret["comment"]: ret["comment"] += ". " ret["comment"] += ". ".join(errors) + "." else: ret["result"] = True # Figure out if we removed any containers as a result of replacing the # network and did not reconnect them. We only would not have reconnected if # a list of containers was passed in the "containers" argument, and there # were containers connected to the network prior to its replacement which # were not part of that list. for cid, c_info in disconnected_containers.items(): if cid not in to_connect: ret["changes"].setdefault("disconnected", []).append(c_info["Name"]) return ret def absent(name): """ Ensure that a network is absent. name Name of the network Usage Example: .. code-block:: yaml network_foo: docker_network.absent """ ret = {"name": name, "changes": {}, "result": False, "comment": ""} try: network = __salt__["docker.inspect_network"](name) except CommandExecutionError as exc: msg = str(exc) if "404" in msg: # Network not present network = None else: ret["comment"] = msg return ret if network is None: ret["result"] = True ret["comment"] = f"Network '{name}' already absent" return ret if __opts__["test"]: ret["result"] = None ret["comment"] = f"Network '{name}' will be removed" return ret return _remove_network(network) def _remove_network(network): """ Remove network, including all connected containers """ ret = {"name": network["Name"], "changes": {}, "result": False, "comment": ""} errors = [] for cid in network["Containers"]: try: cinfo = __salt__["docker.inspect_container"](cid) except CommandExecutionError: # Fall back to container ID cname = cid else: cname = cinfo.get("Name", "").lstrip("/") try: __salt__["docker.disconnect_container_from_network"](cid, network["Name"]) except CommandExecutionError as exc: errors = f"Failed to disconnect container '{cname}' : {exc}" else: ret["changes"].setdefault("disconnected", []).append(cname) if errors: ret["comment"] = "\n".join(errors) return ret try: __salt__["docker.remove_network"](network["Name"]) except CommandExecutionError as exc: ret["comment"] = f"Failed to remove network: {exc}" else: ret["changes"]["removed"] = True ret["result"] = True ret["comment"] = "Removed network '{}'".format(network["Name"]) return ret