Add filter for `network ls` to hide predefined net

Add filter support for `network ls` to hide predefined network,
then user can use "docker network rm `docker network ls -f type=custom`"
to delete a bundle of userdefined networks.

Signed-off-by: Zhang Wei <zhangwei555@huawei.com>
This commit is contained in:
Zhang Wei 2015-11-10 16:57:06 +08:00
parent 1105caa7f1
commit 26dd026bd7
13 changed files with 405 additions and 33 deletions

View File

@ -65,7 +65,7 @@ type apiClient interface {
NetworkCreate(options types.NetworkCreate) (types.NetworkCreateResponse, error)
NetworkDisconnect(networkID, containerID string) error
NetworkInspect(networkID string) (types.NetworkResource, error)
NetworkList() ([]types.NetworkResource, error)
NetworkList(options types.NetworkListOptions) ([]types.NetworkResource, error)
NetworkRemove(networkID string) error
RegistryLogin(auth types.AuthConfig) (types.AuthResponse, error)
ServerVersion() (types.Version, error)

View File

@ -3,8 +3,10 @@ package lib
import (
"encoding/json"
"net/http"
"net/url"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
)
// NetworkCreate creates a new network in the docker host.
@ -44,9 +46,18 @@ func (cli *Client) NetworkDisconnect(networkID, containerID string) error {
}
// NetworkList returns the list of networks configured in the docker host.
func (cli *Client) NetworkList() ([]types.NetworkResource, error) {
func (cli *Client) NetworkList(options types.NetworkListOptions) ([]types.NetworkResource, error) {
query := url.Values{}
if options.Filters.Len() > 0 {
filterJSON, err := filters.ToParam(options.Filters)
if err != nil {
return nil, err
}
query.Set("filters", filterJSON)
}
var networkResources []types.NetworkResource
resp, err := cli.get("/networks", nil, nil)
resp, err := cli.get("/networks", query, nil)
if err != nil {
return networkResources, err
}

View File

@ -7,6 +7,7 @@ import (
"text/tabwriter"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network"
Cli "github.com/docker/docker/cli"
"github.com/docker/docker/opts"
@ -138,12 +139,29 @@ func (cli *DockerCli) CmdNetworkLs(args ...string) error {
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Do not truncate the output")
flFilter := opts.NewListOpts(nil)
cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided")
cmd.Require(flag.Exact, 0)
if err := cmd.ParseFlags(args, true); err != nil {
err := cmd.ParseFlags(args, true)
if err != nil {
return err
}
networkResources, err := cli.client.NetworkList()
// Consolidate all filter flags, and sanity check them early.
// They'll get process after get response from server.
netFilterArgs := filters.NewArgs()
for _, f := range flFilter.GetAll() {
if netFilterArgs, err = filters.ParseFlag(f, netFilterArgs); err != nil {
return err
}
}
options := types.NetworkListOptions{
Filters: netFilterArgs,
}
networkResources, err := cli.client.NetworkList(options)
if err != nil {
return err
}

View File

@ -12,6 +12,7 @@ type Backend interface {
FindNetwork(idName string) (libnetwork.Network, error)
GetNetwork(idName string, by int) (libnetwork.Network, error)
GetNetworksByID(partialID string) []libnetwork.Network
GetAllNetworks() []libnetwork.Network
CreateNetwork(name, driver string, ipam network.IPAM,
options map[string]string) (libnetwork.Network, error)
ConnectContainerToNetwork(containerName, networkName string) error

View File

@ -0,0 +1,110 @@
package network
import (
"fmt"
"regexp"
"strings"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/runconfig"
"github.com/docker/libnetwork"
)
type filterHandler func([]libnetwork.Network, string) ([]libnetwork.Network, error)
var (
// supportedFilters predefined some supported filter handler function
supportedFilters = map[string]filterHandler{
"type": filterNetworkByType,
"name": filterNetworkByName,
"id": filterNetworkByID,
}
// acceptFilters is an acceptable filter flag list
// generated for validation. e.g.
// acceptedFilters = map[string]bool{
// "type": true,
// "name": true,
// "id": true,
// }
acceptedFilters = func() map[string]bool {
ret := make(map[string]bool)
for k := range supportedFilters {
ret[k] = true
}
return ret
}()
)
func filterNetworkByType(nws []libnetwork.Network, netType string) (retNws []libnetwork.Network, err error) {
switch netType {
case "builtin":
for _, nw := range nws {
if runconfig.IsPreDefinedNetwork(nw.Name()) {
retNws = append(retNws, nw)
}
}
case "custom":
for _, nw := range nws {
if !runconfig.IsPreDefinedNetwork(nw.Name()) {
retNws = append(retNws, nw)
}
}
default:
return nil, fmt.Errorf("Invalid filter: 'type'='%s'", netType)
}
return retNws, nil
}
func filterNetworkByName(nws []libnetwork.Network, name string) (retNws []libnetwork.Network, err error) {
for _, nw := range nws {
// exact match (fast path)
if nw.Name() == name {
retNws = append(retNws, nw)
continue
}
// regexp match (slow path)
match, err := regexp.MatchString(name, nw.Name())
if err != nil || !match {
continue
} else {
retNws = append(retNws, nw)
}
}
return retNws, nil
}
func filterNetworkByID(nws []libnetwork.Network, id string) (retNws []libnetwork.Network, err error) {
for _, nw := range nws {
if strings.HasPrefix(nw.ID(), id) {
retNws = append(retNws, nw)
}
}
return retNws, nil
}
// filterAllNetworks filter network list according to user specified filter
// and return user chosen networks
func filterNetworks(nws []libnetwork.Network, filter filters.Args) ([]libnetwork.Network, error) {
// if filter is empty, return original network list
if filter.Len() == 0 {
return nws, nil
}
var displayNet []libnetwork.Network
for fkey, fhandler := range supportedFilters {
errFilter := filter.WalkValues(fkey, func(fval string) error {
passList, err := fhandler(nws, fval)
if err != nil {
return err
}
displayNet = append(displayNet, passList...)
return nil
})
if errFilter != nil {
return nil, errFilter
}
}
return displayNet, nil
}

View File

@ -7,7 +7,6 @@ import (
"golang.org/x/net/context"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
@ -28,29 +27,24 @@ func (n *networkRouter) getNetworksList(ctx context.Context, w http.ResponseWrit
return err
}
list := []*types.NetworkResource{}
netFilters.WalkValues("name", func(name string) error {
if nw, err := n.backend.GetNetwork(name, daemon.NetworkByName); err == nil {
list = append(list, buildNetworkResource(nw))
} else {
logrus.Errorf("failed to get network for filter=%s : %v", name, err)
}
return nil
})
netFilters.WalkValues("id", func(id string) error {
for _, nw := range n.backend.GetNetworksByID(id) {
list = append(list, buildNetworkResource(nw))
}
return nil
})
if !netFilters.Include("name") && !netFilters.Include("id") {
nwList := n.backend.GetNetworksByID("")
for _, nw := range nwList {
list = append(list, buildNetworkResource(nw))
if netFilters.Len() != 0 {
if err := netFilters.Validate(acceptedFilters); err != nil {
return err
}
}
list := []*types.NetworkResource{}
nwList := n.backend.GetAllNetworks()
displayable, err := filterNetworks(nwList, netFilters)
if err != nil {
return err
}
for _, nw := range displayable {
list = append(list, buildNetworkResource(nw))
}
return httputils.WriteJSON(w, http.StatusOK, list)
}

View File

@ -86,6 +86,11 @@ type EventsOptions struct {
Filters filters.Args
}
// NetworkListOptions holds parameters to filter the list of networks with.
type NetworkListOptions struct {
Filters filters.Args
}
// HijackedResponse holds connection information for a hijacked request.
type HijackedResponse struct {
Conn net.Conn

View File

@ -85,6 +85,19 @@ func (daemon *Daemon) GetNetworksByID(partialID string) []libnetwork.Network {
return list
}
// GetAllNetworks returns a list containing all networks
func (daemon *Daemon) GetAllNetworks() []libnetwork.Network {
c := daemon.netController
list := []libnetwork.Network{}
l := func(nw libnetwork.Network) bool {
list = append(list, nw)
return false
}
c.WalkNetworks(l)
return list
}
// CreateNetwork creates a network with the given name, driver and other optional parameters
func (daemon *Daemon) CreateNetwork(name, driver string, ipam network.IPAM, options map[string]string) (libnetwork.Network, error) {
c := daemon.netController

View File

@ -109,6 +109,7 @@ This section lists each version from latest to oldest. Each listing includes a
the push or pull completes.
* `POST /containers/create` now allows you to set a read/write rate limit for a
device (in bytes per second or IO per second).
* `GET /networks` now supports filtering by `name`, `id` and `type`.
### v1.21 API changes

View File

@ -2728,7 +2728,7 @@ Status Codes
**Example request**:
GET /networks HTTP/1.1
GET /networks?filters={"type":{"custom":true}} HTTP/1.1
**Example response**:
@ -2794,11 +2794,12 @@ Content-Type: application/json
]
```
Query Parameters:
- **filters** - JSON encoded value of the filters (a `map[string][]string`) to process on the networks list. Available filters: `name=[network-names]` , `id=[network-ids]`
- **filters** - JSON encoded network list filter. The filter value is one of:
- `name=<network-name>` Matches all or part of a network name.
- `id=<network-id>` Matches all or part of a network id.
- `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks.
Status Codes:

View File

@ -13,6 +13,7 @@ parent = "smn_cli"
Usage: docker network ls [OPTIONS]
Lists all the networks created by the user
-f, --filter=[] Filter output based on conditions provided
--help=false Print usage
--no-trunc=false Do not truncate the output
-q, --quiet=false Only display numeric IDs
@ -38,8 +39,91 @@ NETWORK ID NAME
c288470c46f6c8949c5f7e5099b5b7947b07eabe8d9a27d79a9cbf111adcbf47 host host
7b369448dccbf865d397c8d2be0cda7cf7edc6b0945f77d2529912ae917a0185 bridge bridge
95e74588f40db048e86320c6526440c504650a1ff3e9f7d60a497c4d2163e5bd foo bridge
63d1ff1f77b07ca51070a8c227e962238358bd310bde1529cf62e6c307ade161 dev bridge
```
## Filtering
The filtering flag (`-f` or `--filter`) format is a `key=value` pair. If there
is more than one filter, then pass multiple flags (e.g. `--filter "foo=bar" --filter "bif=baz"`).
Multiple filter flags are combined as an `OR` filter. For example,
`-f type=custom -f type=builtin` returns both `custom` and `builtin` networks.
The currently supported filters are:
* id (network's id)
* name (network's name)
* type (custom|builtin)
#### Type
The `type` filter supports two values; `builtin` displays predefined networks
(`bridge`, `none`, `host`), whereas `custom` displays user defined networks.
The following filter matches all user defined networks:
```bash
$ docker network ls --filter type=custom
NETWORK ID NAME DRIVER
95e74588f40d foo bridge
63d1ff1f77b0 dev bridge
```
By having this flag it allows for batch cleanup. For example, use this filter
to delete all user defined networks:
```bash
$ docker network rm `docker network ls --filter type=custom -q`
```
A warning will be issued when trying to remove a network that has containers
attached.
#### Name
The `name` filter matches on all or part of a network's name.
The following filter matches all networks with a name containing the `foobar` string.
```bash
$ docker network ls --filter name=foobar
NETWORK ID NAME DRIVER
06e7eef0a170 foobar bridge
```
You can also filter for a substring in a name as this shows:
```bash
$ docker ps --filter name=foo
NETWORK ID NAME DRIVER
95e74588f40d foo bridge
06e7eef0a170 foobar bridge
```
#### ID
The `id` filter matches on all or part of a network's ID.
The following filter matches all networks with a name containing the
`06e7eef01700` string.
```bash
$ docker network ls --filter id=63d1ff1f77b07ca51070a8c227e962238358bd310bde1529cf62e6c307ade161
NETWORK ID NAME DRIVER
63d1ff1f77b0 dev bridge
```
You can also filter for a substring in a ID as this shows:
```bash
$ docker ps --filter id=95e74588f40d
NETWORK ID NAME DRIVER
95e74588f40d foo bridge
$ docker ps --filter id=95e
NETWORK ID NAME DRIVER
95e74588f40d foo bridge
```
## Related information

View File

@ -10,6 +10,7 @@ import (
"net/http"
"net/http/httptest"
"os"
"sort"
"strings"
"github.com/docker/docker/api/types"
@ -242,6 +243,25 @@ func isNwPresent(c *check.C, name string) bool {
return false
}
// assertNwList checks network list retrived with ls command
// equals to expected network list
// note: out should be `network ls [option]` result
func assertNwList(c *check.C, out string, expectNws []string) {
lines := strings.Split(out, "\n")
var nwList []string
for _, line := range lines[1 : len(lines)-1] {
netFields := strings.Fields(line)
// wrap all network name in nwList
nwList = append(nwList, netFields[1])
}
// first need to sort out and expected
sort.StringSlice(nwList).Sort()
sort.StringSlice(expectNws).Sort()
// network ls should contains all expected networks
c.Assert(nwList, checker.DeepEquals, expectNws)
}
func getNwResource(c *check.C, name string) *types.NetworkResource {
out, _ := dockerCmd(c, "network", "inspect", name)
nr := []types.NetworkResource{}
@ -257,6 +277,32 @@ func (s *DockerNetworkSuite) TestDockerNetworkLsDefault(c *check.C) {
}
}
func (s *DockerNetworkSuite) TestDockerNetworkLsFilter(c *check.C) {
out, _ := dockerCmd(c, "network", "create", "dev")
defer func() {
dockerCmd(c, "network", "rm", "dev")
}()
containerID := strings.TrimSpace(out)
// filter with partial ID and partial name
// only show 'bridge' and 'dev' network
out, _ = dockerCmd(c, "network", "ls", "-f", "id="+containerID[0:5], "-f", "name=dge")
assertNwList(c, out, []string{"dev", "bridge"})
// only show built-in network (bridge, none, host)
out, _ = dockerCmd(c, "network", "ls", "-f", "type=builtin")
assertNwList(c, out, []string{"bridge", "none", "host"})
// only show custom networks (dev)
out, _ = dockerCmd(c, "network", "ls", "-f", "type=custom")
assertNwList(c, out, []string{"dev"})
// show all networks with filter
// it should be equivalent of ls without option
out, _ = dockerCmd(c, "network", "ls", "-f", "type=custom", "-f", "type=builtin")
assertNwList(c, out, []string{"dev", "bridge", "host", "none"})
}
func (s *DockerNetworkSuite) TestDockerNetworkCreateDelete(c *check.C) {
dockerCmd(c, "network", "create", "test")
assertNwIsAvailable(c, "test")

View File

@ -6,6 +6,7 @@ docker-network-ls - list networks
# SYNOPSIS
**docker network ls**
[**-f**|**--filter**[=*[]*]]
[**--no-trunc**[=*true*|*false*]]
[**-q**|**--quiet**[=*true*|*false*]]
[**--help**]
@ -16,7 +17,7 @@ Lists all the networks the Engine `daemon` knows about. This includes the
networks that span across multiple hosts in a cluster, for example:
```bash
$ sudo docker network ls
$ docker network ls
NETWORK ID NAME DRIVER
7fca4eb8c647 bridge bridge
9f904ee27bf5 none null
@ -27,16 +28,103 @@ networks that span across multiple hosts in a cluster, for example:
Use the `--no-trunc` option to display the full network id:
```bash
docker network ls --no-trunc
$ docker network ls --no-trunc
NETWORK ID NAME DRIVER
18a2866682b85619a026c81b98a5e375bd33e1b0936a26cc497c283d27bae9b3 none null
c288470c46f6c8949c5f7e5099b5b7947b07eabe8d9a27d79a9cbf111adcbf47 host host
7b369448dccbf865d397c8d2be0cda7cf7edc6b0945f77d2529912ae917a0185 bridge bridge
95e74588f40db048e86320c6526440c504650a1ff3e9f7d60a497c4d2163e5bd foo bridge
63d1ff1f77b07ca51070a8c227e962238358bd310bde1529cf62e6c307ade161 dev bridge
```
## Filtering
The filtering flag (`-f` or `--filter`) format is a `key=value` pair. If there
is more than one filter, then pass multiple flags (e.g. `--filter "foo=bar" --filter "bif=baz"`).
Multiple filter flags are combined as an `OR` filter. For example,
`-f type=custom -f type=builtin` returns both `custom` and `builtin` networks.
The currently supported filters are:
* id (network's id)
* name (network's name)
* type (custom|builtin)
#### Type
The `type` filter supports two values; `builtin` displays predefined networks
(`bridge`, `none`, `host`), whereas `custom` displays user defined networks.
The following filter matches all user defined networks:
```bash
$ docker network ls --filter type=custom
NETWORK ID NAME DRIVER
95e74588f40d foo bridge
63d1ff1f77b0 dev bridge
```
By having this flag it allows for batch cleanup. For example, use this filter
to delete all user defined networks:
```bash
$ docker network rm `docker network ls --filter type=custom -q`
```
A warning will be issued when trying to remove a network that has containers
attached.
#### Name
The `name` filter matches on all or part of a network's name.
The following filter matches all networks with a name containing the `foobar` string.
```bash
$ docker network ls --filter name=foobar
NETWORK ID NAME DRIVER
06e7eef0a170 foobar bridge
```
You can also filter for a substring in a name as this shows:
```bash
$ docker ps --filter name=foo
NETWORK ID NAME DRIVER
95e74588f40d foo bridge
06e7eef0a170 foobar bridge
```
#### ID
The `id` filter matches on all or part of a network's ID.
The following filter matches all networks with a name containing the
`06e7eef01700` string.
```bash
$ docker network ls --filter id=63d1ff1f77b07ca51070a8c227e962238358bd310bde1529cf62e6c307ade161
NETWORK ID NAME DRIVER
63d1ff1f77b0 dev bridge
```
You can also filter for a substring in a ID as this shows:
```bash
$ docker ps --filter id=95e74588f40d
NETWORK ID NAME DRIVER
95e74588f40d foo bridge
$ docker ps --filter id=95e
NETWORK ID NAME DRIVER
95e74588f40d foo bridge
```
# OPTIONS
**-f**, **--filter**=*[]*
filter output based on conditions provided.
**--no-trunc**=*true*|*false*
Do not truncate the output