Exemple #1
0
    def prune(
        self, filters: Optional[Dict[str, str]] = None  # pylint: disable=unused-argument
    ) -> Dict[str, Any]:
        """Delete unused volumes.

        Args:
            filters: Criteria for selecting volumes to delete. Ignored.

        Raises:
            APIError when service reports error
        """
        response = self.client.post("/volumes/prune")
        data = response.json()

        if response.status_code != 200:
            raise APIError(data["cause"], response=response, explanation=data["message"])

        volumes: List[str] = list()
        space_reclaimed = 0
        for item in data:
            if "Err" in item:
                raise APIError(
                    item["Err"],
                    response=response,
                    explanation=f"""Failed to prune volume '{item.get("Id")}'""",
                )
            volumes.append(item.get("Id"))
            space_reclaimed += item["Size"]

        return {"VolumesDeleted": volumes, "SpaceReclaimed": space_reclaimed}
    def prune(self,
              filters: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
        """Delete unused Pods.

        Returns:
            Dictionary Keys:
                - PodsDeleted (List[str]): List of pod ids deleted.
                - SpaceReclaimed (int): Always zero.

        Raises:
            APIError when service reports error
        """
        response = self.client.post(
            "/pods/prune", params={"filters": api.prepare_filters(filters)})
        body = response.json()

        if response.status_code != requests.codes.okay:
            raise APIError(body["cause"],
                           response=response,
                           explanation=body["message"])

        deleted: List[str] = list()
        for item in body:
            if item["Err"] is not None:
                raise APIError(
                    item["Err"],
                    response=response,
                    explanation=f"""Failed to prune network '{item["Id"]}'""",
                )
            deleted.append(item["Id"])
        return {"PodsDeleted": deleted, "SpaceReclaimed": 0}
    def prune(self, filters: Mapping[str, str] = None) -> Dict[str, Any]:
        """Delete stopped containers.

        Args:
            filters: Dict of criteria for determining containers to remove. Available keys are:
                - until (str): Delete containers before this time
                - label (List[str]): Labels associated with containers

        Returns:
            List of deleted container id's and the freed disk space in bytes.

        Raises:
            APIError: If service reports an error
        """
        params = dict()
        if filters is not None:
            params = {"filters", api.format_filters(filters)}

        response = self.client.post("/containers/prune", params=params)
        body = response.json()

        if response.status_code != 200:
            raise APIError(body["cause"], response=response, explanation=body["message"])

        results = {"ContainersDeleted": [], "SpaceReclaimed": 0}
        for entry in body:
            if entry.get("error", None) is not None:
                raise APIError(entry["error"], response=response, explanation=entry["error"])

            results["ContainersDeleted"].append(entry["id"])
            results["SpaceReclaimed"] += entry["space"]
        return results
Exemple #4
0
    def prune(self, filters: Mapping[str, str] = None) -> Dict[str, Any]:
        """Delete stopped containers.

        Args:
            filters: Criteria for determining containers to remove. Available keys are:
                - until (str): Delete containers before this time
                - label (List[str]): Labels associated with containers

        Returns:
            Keys:
                - ContainersDeleted (List[str]): Id's of deleted containers.
                - SpaceReclaimed (int): Amount of disk space reclaimed in bytes.

        Raises:
            APIError: If service reports an error
        """
        params = {"filters": api.prepare_filters(filters)}
        response = self.client.post("/containers/prune", params=params)
        body = response.json()

        if response.status_code != requests.codes.okay:
            raise APIError(body["cause"],
                           response=response,
                           explanation=body["message"])

        results = {"ContainersDeleted": [], "SpaceReclaimed": 0}
        for entry in body:
            if entry.get("error", None) is not None:
                raise APIError(entry["error"],
                               response=response,
                               explanation=entry["error"])

            results["ContainersDeleted"].append(entry["Id"])
            results["SpaceReclaimed"] += entry["Size"]
        return results
Exemple #5
0
    def create(self, name: Optional[str] = None, **kwargs) -> Volume:
        """Create an Volume.

        Args:
            name: Name given to new volume

        Keyword Args:
            driver (str): Volume driver to use
            driver_opts (Dict[str, str]): Options to use with driver
            labels (Dict[str, str]): Labels to apply to volume

        Raises:
            APIError when service reports error
        """
        data = {
            "Driver": kwargs.get("driver", None),
            "Labels": kwargs.get("labels", None),
            "Name": name,
            "Options": kwargs.get("driver_opts"),
        }
        # Strip out any keys without a value
        data = {k: v for (k, v) in data.items() if v is not None}
        contents = json.dumps(data)

        response = self.client.post(
            "/volumes/create",
            data=contents,
            headers={"Content-Type": "application/json"},
        )
        data = response.json()

        if response.status_code == 201:
            return self.prepare_model(data)

        raise APIError(data["cause"], response=response, explanation=data["message"])
Exemple #6
0
    def disconnect(self, container: Union[str, Container], **kwargs) -> None:
        """Disconnect given container from this network.

        Args:
            container: To remove from this Network

        Keyword Args:
            force (bool): Force operation

        Raises:
            APIError when Podman service reports an error
        """
        compatible = kwargs.get("compatible", True)

        if isinstance(container, Container):
            container = container.id

        data = {"Container": container, "Force": kwargs.get("force")}
        response = self.client.post(f"/networks/{self.name}/disconnect",
                                    data=json.dumps(data),
                                    compatible=compatible)
        if response.status_code == 200:
            return

        data = response.json()
        raise APIError(data["cause"],
                       response=response,
                       explanation=data["message"])
Exemple #7
0
    def top(self, **kwargs) -> Union[Iterator[Dict[str, Any]], Dict[str, Any]]:
        """Report on running processes in the container.

        Keyword Args:
            ps_args (str): When given, arguments will be passed to ps
            stream (bool): When True, repeatedly return results. Default: False

        Raises:
            NotFound: when the container no longer exists
            APIError: when the service reports an error
        """
        params = {
            "ps_args": kwargs.get("ps_args"),
            "stream": kwargs.get("stream", False),
        }
        response = self.client.get(f"/containers/{self.id}/top", params=params)

        if response.status_code != requests.codes.okay:
            body = response.json()

            if response.status_code == requests.codes.not_found:
                raise NotFound(body["cause"], response=response, explanation=body["message"])
            raise APIError(body["cause"], response=response, explanation=body["message"])

        if params["stream"]:
            self._top_helper(response)

        return response.json()
Exemple #8
0
    def wait(self, **kwargs) -> Dict[str, Any]:
        """Block until container enters given state.

        Keyword Args:
            condition (str): Container state on which to release, values:
                not-running (default), next-exit or removed.
            timeout (int): Number of seconds to wait for container to stop.

        Returns:
              API response as a dict, including the container's exit code under the key StatusCode.

        Raises:
              ReadTimeoutError: If the timeout is exceeded.
              APIError: If the service returns as error.
        """
        params = {"condition": kwargs.get("condition", None)}
        response = self.client.post(f"/containers/{self.id}/wait",
                                    params=params)

        if response.status_code == 204:
            return

        body = response.json()
        raise APIError(body["cause"],
                       response=response,
                       explanation=body["message"])
Exemple #9
0
    def export(self,
               chunk_size: int = api.DEFAULT_CHUNK_SIZE) -> Iterator[bytes]:
        """Download container's filesystem contents as a tar archive.

        Args:
            chunk_size: <= number of bytes to return for each iteration of the generator.

        Yields:
            tarball in size/chunk_size chunks

        Raises:
            NotFound when container has been removed from service
            APIError when service reports an error
        """
        response = self.client.get(f"/containers/{self.id}/export",
                                   stream=True)

        if response.status_code != 200:
            body = response.json()
            if response.status_code == 404:
                raise NotFound(body["cause"],
                               response=response,
                               explanation=body["message"])
            raise APIError(body["cause"],
                           response=response,
                           explanation=body["message"])

        for out in response.iter_content(chunk_size=chunk_size):
            yield out
Exemple #10
0
    def stop(self, **kwargs) -> None:
        """Stop container.

        Keyword Args:
            all (bool): When True, stop all containers. Default: False (Podman only)
            ignore (bool): When True, ignore error if container already stopped (Podman only)
            timeout (int): Number of seconds to wait on container to stop before killing it.
        """
        params = {"all": kwargs.get("all"), "timeout": kwargs.get("timeout")}

        connection_timeout: float = api.DEFAULT_TIMEOUT
        if params.get("timeout"):
            connection_timeout += float(params["timeout"])

        response = self.client.post(f"/containers/{self.id}/stop",
                                    params=params,
                                    timeout=connection_timeout)
        response.raise_for_status()

        if response.status_code == requests.codes.no_content:
            return

        if response.status_code == requests.codes.not_modified:
            if kwargs.get("ignore", False):
                return

        body = response.json()
        raise APIError(body["cause"],
                       response=response,
                       explanation=body["message"])
Exemple #11
0
    def get(self, network_id: str, *_, **kwargs) -> Network:  # pylint: disable=arguments-differ
        """Return information for network network_id.

        Args:
            network_id: Network name or id.

        Keyword Args:
            compatible (bool): Should compatible API be used. Default: True

        Raises:
            NotFound: Network does not exist.
            APIError: Error returned by service.

        Note:
            The compatible API is used, this allows the server to provide dynamic fields.
                id is the most important example.
        """
        compatible = kwargs.get("compatible", True)

        path = f"/networks/{network_id}" + ("" if compatible else "/json")

        response = self.client.get(path, compatible=compatible)
        body = response.json()

        if response.status_code != 200:
            if response.status_code == 404:
                raise NotFound(body["cause"], response=response, explanation=body["message"])
            raise APIError(body["cause"], response=response, explanation=body["message"])

        if not compatible:
            body = body[0]

        return self.prepare_model(body)
Exemple #12
0
    def list(self, *_, **kwargs) -> List[Volume]:
        """Report on volumes.

        Keyword Args:
            filters (Dict[str, str]): criteria to filter Volume list
                - driver (str): filter volumes by their driver
                - label (Dict[str, str]): filter by label and/or value
                - name (str): filter by volume's name
        """
        filters = api.prepare_filters(kwargs.get("filters"))
        response = self.client.get("/volumes", params={"filters": filters})

        if response.status_code == requests.codes.not_found:
            return []

        data = response.json()
        if response.status_code != requests.codes.okay:
            raise APIError(data["cause"],
                           response=response,
                           explanation=data["message"])

        volumes: List[Volume] = list()
        for item in data:
            volumes.append(self.prepare_model(item))
        return volumes
Exemple #13
0
    def remove(self,
               name: Union[Volume, str],
               force: Optional[bool] = None) -> None:
        """Delete a volume.

        Args:
            name: Identifier for Volume to be deleted.
            force: When true, force deletion of in-use volume

        Raises:
            APIError: when service reports an error

        Notes:
            Podman only.
        """
        if isinstance(name, Volume):
            name = name.name
        response = self.client.delete(f"/volumes/{name}",
                                      params={"force": force})

        if response.status_code == requests.codes.no_content:
            return

        data = response.json()
        if response.status_code == requests.codes.not_found:
            raise NotFound(data["cause"],
                           response=response,
                           explanation=data["message"])
        raise APIError(data["cause"],
                       response=response,
                       explanation=data["message"])
Exemple #14
0
    def stats(self, **kwargs) -> Union[Sequence[Dict[str, bytes]], bytes]:
        """Return statistics for container.

        Keyword Args:
            decode (bool): If True and stream is True, stream will be decoded into dict's.
                Default: False.
            stream (bool): Stream statistics until cancelled. Default: True.

        Raises:
            APIError when service reports an error
        """
        # FIXME Errors in stream are not handled, need content and json to read Errors.
        stream = kwargs.get("stream", True)
        decode = kwargs.get("decode", False)

        params = {
            "containers": self.id,
            "stream": stream,
        }

        response = self.client.get("/containers/stats", params=params)

        if response.status_code != requests.codes.okay:
            body = response.json()
            if response.status_code == requests.codes.not_found:
                raise NotFound(body["cause"], response=response, explanation=body["message"])
            raise APIError(body["cause"], response=response, explanation=body["message"])

        if stream:
            return self._stats_helper(decode, response.iter_lines())

        with io.StringIO() as buffer:
            for entry in response.text:
                buffer.writer(json.dumps(entry) + "\n")
            return buffer.getvalue()
Exemple #15
0
    def wait(self, **kwargs) -> Dict[str, Any]:
        """Block until the container enters given state.

        Keyword Args:
            condition (str): Container state on which to release, values:
                not-running (default), next-exit or removed.
            timeout (int): Number of seconds to wait for the container to stop.

        Returns:
              Keys:
                - StatusCode (int): Container's exit code
                - Error["Message"] (str): Error message from container

        Raises:
              NotFound: When Container not found
              ReadTimeoutError: When timeout is exceeded
              APIError: When service returns an error
        """
        condition = kwargs.get("condition")
        if isinstance(condition, str):
            condition = [condition]

        response = self.client.post(f"/containers/{self.id}/wait", params={"condition": condition})
        body = response.json()

        if response.status_code == requests.codes.okay:
            return body

        if response.status_code == requests.codes.not_found:
            raise NotFound(body["cause"], response=response, explanation=body["message"])
        raise APIError(body["cause"], response=response, explanation=body["message"])
Exemple #16
0
    def get_archive(
        self,
        path: str,
        chunk_size: int = api.DEFAULT_CHUNK_SIZE
    ) -> Tuple[Iterable, Dict[str, Any]]:
        """Download a file or folder from the container's filesystem.

        Args:
            path: Path to file or folder.
            chunk_size: <= number of bytes to return for each iteration of the generator.

        Returns:
            First item is a raw tar data stream.
            Second item is a dict containing stat information on the specified path.
        """
        response = self.client.get(f"/containers/{self.id}/archive",
                                   params={"path": [path]})
        if response.status_code != 200:
            body = response.json()
            if response.status_code == 404:
                raise NotFound(body["cause"],
                               response=response,
                               explanation=body["message"])
            raise APIError(body["cause"],
                           response=response,
                           explanation=body["message"])

        stat = response.headers.get('x-docker-container-path-stat', None)
        stat = api.decode_header(stat)
        return response.iter_content(chunk_size=chunk_size), stat
Exemple #17
0
    def stop(self, **kwargs):
        """Stop container.

        Keyword Args:
            all (bool): When True, stop all containers. Default: False (Podman only)
            ignore (bool): When True, ignore error if container already stopped (Podman only)
            timeout (int): Number of seconds to wait on container to stop before killing it.
        """
        connection_timeout = api.DEFAULT_TIMEOUT

        params = {}
        if "all" in kwargs:
            params["all"] = kwargs["all"]
        if "timeout" in kwargs:
            params["timeout"] = kwargs["timeout"]
            connection_timeout += float(kwargs["timeout"])

        response = self.client.post(f"/containers/{self.id}/stop",
                                    params=params,
                                    timeout=connection_timeout)
        if response.status_code == 204:
            return

        if response.status_code == 304:
            if kwargs.get("ignore", False):
                return

        body = response.json()
        raise APIError(body["cause"],
                       response=response,
                       explanation=body["message"])
Exemple #18
0
    def list(self, **kwargs) -> List[Pod]:
        """Report on pods.

        Keyword Args:
            filters (Mapping[str, str]): Criteria for listing pods. Available filters:
                - ctr-ids (List[str]): List of container ids to filter by.
                - ctr-names (List[str]): List of container names to filter by.
                - ctr-number (List[int]): list pods with given number of containers.
                - ctr-status (List[str]): List pods with containers in given state.
                    Legal values are: "created", "running", "paused",
                    "stopped", "exited", "unknown"
                - id (str) - List pod with this id.
                - name (str) - List pod with this name.
                - status (List[str]): List pods in given state. Legal values are: "created",
                    "running", "paused", "stopped", "exited", "unknown"
                - label (List[str]): List pods with given labels.
                - network (List[str]): List pods associated with given Network Ids (not Names).

        Raises:
            APIError: Error returned by service.
        """
        params = {"filters": api.prepare_filters(kwargs.get("filters"))}
        response = self.client.get("/pods/json", params=params)
        body = response.json()

        if response.status_code != requests.codes.okay:
            raise APIError(body["cause"],
                           response=response,
                           explanation=body["message"])

        pods: List[Pod] = list()
        for item in body:
            pods.append(self.prepare_model(attrs=item))
        return pods
Exemple #19
0
    def create(self, name: Optional[str] = None, **kwargs) -> Volume:
        """Create a Volume.

        Args:
            name: Name given to new volume

        Keyword Args:
            driver (str): Volume driver to use
            driver_opts (Dict[str, str]): Options to use with driver
            labels (Dict[str, str]): Labels to apply to volume

        Raises:
            APIError when service reports error
        """
        data = {
            "Driver": kwargs.get("driver"),
            "Labels": kwargs.get("labels"),
            "Name": name,
            "Options": kwargs.get("driver_opts"),
        }
        response = self.client.post(
            "/volumes/create",
            data=api.prepare_body(data),
            headers={"Content-Type": "application/json"},
        )
        data = response.json()

        if response.status_code == requests.codes.created:
            return self.prepare_model(attrs=data)

        raise APIError(data["cause"],
                       response=response,
                       explanation=data["message"])
Exemple #20
0
    def stats(self, **kwargs) -> Dict[str, Any]:
        """Resource usage statistics for the containers in pods.

        Keyword Args:
            all (bool): Provide statistics for all running pods.
            name (Union[str, List[str]]): Pods to include in report.

        Raises:
            NotFound when pod not found.
            APIError when service reports an error.
        """
        if "all" in kwargs and "name" in kwargs:
            raise ValueError(
                "Keywords 'all' and 'name' are mutually exclusive.")

        params = {
            "all": kwargs.get("all"),
            "namesOrIDs": kwargs.get("name"),
        }
        response = self.client.get("/pods/stats", params=params)
        body = response.json()

        if response.status_code == requests.codes.okay:
            return body

        if response.status_code == requests.codes.not_found:
            raise NotFound(body["cause"],
                           response=response,
                           explanation=body["message"])
        raise APIError(body["cause"],
                       response=response,
                       explanation=body["message"])
Exemple #21
0
    def remove(self, force: Optional[bool] = None, **kwargs) -> None:
        """Remove this network.

        Args:
            force: Remove network and any associated networks

        Keyword Args:
            compatible (bool): Should compatible API be used. Default: True

        Raises:
            APIError when Podman service reports an error
        """
        compatible = kwargs.get("compatible", True)
        response = self.client.delete(f"/networks/{self.name}",
                                      params={"force": force},
                                      compatible=compatible)

        if (response.status_code == requests.codes.no_content
                or response.status_code == requests.codes.okay):
            return

        body = response.json()
        raise APIError(body["cause"],
                       response=response,
                       explanation=body["message"])
Exemple #22
0
    def kill(self, signal: Union[str, int, None] = None) -> None:
        """Send signal to container. """
        response = self.client.post(f"/containers/{self.id}/kill", params={"signal": signal})
        if response.status_code == requests.codes.no_content:
            return

        body = response.json()
        raise APIError(body["cause"], response=response, explanation=body["message"])
Exemple #23
0
    def unpause(self) -> None:
        """Unpause processes in container."""
        response = self.client.post(f"/containers/{self.id}/unpause")
        if response.status_code == requests.codes.no_content:
            return

        body = response.json()
        raise APIError(body["cause"], response=response, explanation=body["message"])
Exemple #24
0
    def info(self, *_, **__) -> Dict[str, Any]:
        """Returns information on Podman service."""
        response = self.client.get("/system/info")
        body = response.json()

        if response.status_code == 200:
            return body

        raise APIError(body["cause"], response=response, explanation=body["message"])
Exemple #25
0
    def prune(self,
              filters: Optional[Dict[str, Any]] = None,
              **kwargs) -> Dict[str, Any]:
        """Delete unused Networks.

        Args:
            filters: Criteria for selecting volumes to delete. Ignored.


        Keyword Args:
            compatible (bool): Should compatible API be used. Default: True

        Raises:
            APIError when service reports error

        Notes:
            SpaceReclaimed always reported as 0
        """
        compatible = kwargs.get("compatible", True)

        response = self.client.post("/networks/prune",
                                    filters=api.prepare_filters(filters),
                                    compatible=compatible)
        body = response.json()

        if response.status_code != requests.codes.okay:
            raise APIError(body["cause"],
                           response=response,
                           explanation=body["message"])

        if compatible:
            return body

        deleted = list()
        for item in body:
            if item["Error"] is not None:
                raise APIError(
                    item["Error"],
                    response=response,
                    explanation=f"""Failed to prune network '{item["Name"]}'""",
                )
            deleted.append(item["Name"])

        return {"NetworksDeleted": deleted, "SpaceReclaimed": 0}
Exemple #26
0
    def pause(self) -> None:
        """Pause processes within the container."""
        response = self.client.post(f"/containers/{self.id}/pause")
        if response.status_code == requests.codes.no_content:
            return

        body = response.json()
        if response.status_code == requests.codes.not_found:
            raise NotFound(body["cause"], response=response, explanation=body["message"])
        raise APIError(body["cause"], response=response, explanation=body["message"])
Exemple #27
0
    def prune(self, filters: Optional[Dict[str, str]] = None):
        """Delete unused Pods."""
        response = self.client.post(
            "/pods/prune", params={"filters": api.format_filters(filters)})
        if response.status_code == 200:
            return

        body = response.json()
        raise APIError(body["cause"],
                       response=response,
                       explanation=body["message"])
Exemple #28
0
    def list(self, **kwargs) -> List[Container]:
        """Report on containers.

        Keyword Args:
            all: If False, only show running containers. Default: False.
            since: Show containers created after container name or id given.
            before: Show containers created before container name or id given.
            limit: Show last N created containers.
            filters: Filter container reported.
                Available filters:

                - exited (int): Only containers with specified exit code
                - status (str): One of restarting, running, paused, exited
                - label (Union[str, List[str]]): Format either "key", "key=value" or a list of such.
                - id (str): The id of the container.
                - name (str): The name of the container.
                - ancestor (str): Filter by container ancestor. Format of
                    <image-name>[:tag], <image-id>, or <image@digest>.
                - before (str): Only containers created before a particular container.
                    Give the container name or id.
                - since (str): Only containers created after a particular container.
                    Give container name or id.
            sparse: Ignored
            ignore_removed: If True, ignore failures due to missing containers.

        Raises:
            APIError: If service returns an error.
        """
        params = {
            "all": kwargs.get("all"),
            "filters": kwargs.get("filters", dict()),
            "limit": kwargs.get("limit"),
        }
        if "before" in kwargs:
            params["filters"]["before"] = kwargs.get("before")
        if "since" in kwargs:
            params["filters"]["since"] = kwargs.get("since")

        # filters formatted last because some kwargs may need to be mapped into filters
        params["filters"] = api.prepare_filters(params["filters"])

        response = self.client.get("/containers/json", params=params)
        body = response.json()

        if response.status_code != requests.codes.okay:
            raise APIError(body["cause"],
                           response=response,
                           explanation=body["message"])

        containers: List[Container] = list()
        for item in body:
            containers.append(self.prepare_model(attrs=item))
        return containers
Exemple #29
0
    def df(self) -> Dict[str, Any]:  # pylint: disable=invalid-name
        """Disk usage by Podman resources.

        Returns:
            dict: Keyed by resource categories and their data usage.
        """
        response = self.client.get("/system/df")
        body = response.json()

        if response.status_code == 200:
            return body

        raise APIError(body["cause"], response=response, explanation=body["message"])
Exemple #30
0
    def list(self, **kwargs) -> List[Network]:
        """Report on networks.

        Keyword Args:
            names (List[str]): List of names to filter by.
            ids (List[str]): List of ids to filter by.
            filters (Mapping[str,str]): Criteria for listing networks.
                Available filters:
                - driver="bridge": Matches a network's driver. Only "bridge" is supported.
                - label=(Union[str, List[str]]): format either "key", "key=value"
                    or a list of such.
                - type=(str): Filters networks by type, legal values are:
                    - "custom"
                    - "builtin"
                - plugin=(List[str]]): Matches CNI plugins included in a network,
                    legal values are (Podman only):
                        - bridge
                        - portmap
                        - firewall
                        - tuning
                        - dnsname
                        - macvlan
            greedy (bool): Fetch more details for each network individually.
                You might want this to get the containers attached to them. (ignored)

        Raises:
            APIError: Error returned by service.
        """
        compatible = kwargs.get("compatible", True)

        filters = kwargs.get("filters", dict())
        filters["name"] = kwargs.get("names")
        filters["id"] = kwargs.get("ids")
        filters = api.prepare_filters(filters)

        params = {"filters": filters}
        path = f"/networks{'' if compatible else '/json'}"

        response = self.client.get(path, params=params, compatible=compatible)
        body = response.json()

        if response.status_code != requests.codes.okay:
            raise APIError(body["cause"],
                           response=response,
                           explanation=body["message"])

        nets: List[Network] = list()
        for item in body:
            nets.append(self.prepare_model(item))
        return nets