package compat import ( "encoding/json" "net" "net/http" "os" "strings" "syscall" "time" "github.com/containernetworking/cni/libcni" "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/network" "github.com/containers/podman/v2/pkg/api/handlers/utils" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/domain/infra/abi" "github.com/docker/docker/api/types" dockerNetwork "github.com/docker/docker/api/types/network" "github.com/gorilla/schema" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) func InspectNetwork(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) // FYI scope and version are currently unused but are described by the API // Leaving this for if/when we have to enable these // query := struct { // scope string // verbose bool // }{ // // override any golang type defaults // } // decoder := r.Context().Value("decoder").(*schema.Decoder) // if err := decoder.Decode(&query, r.URL.Query()); err != nil { // utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) // return // } config, err := runtime.GetConfig() if err != nil { utils.InternalServerError(w, err) return } name := utils.GetName(r) _, err = network.InspectNetwork(config, name) if err != nil { utils.NetworkNotFound(w, name, err) return } report, err := getNetworkResourceByName(name, runtime) if err != nil { utils.InternalServerError(w, err) return } utils.WriteResponse(w, http.StatusOK, report) } func getNetworkResourceByName(name string, runtime *libpod.Runtime) (*types.NetworkResource, error) { var ( ipamConfigs []dockerNetwork.IPAMConfig ) config, err := runtime.GetConfig() if err != nil { return nil, err } containerEndpoints := map[string]types.EndpointResource{} // Get the network path so we can get created time networkConfigPath, err := network.GetCNIConfigPathByName(config, name) if err != nil { return nil, err } f, err := os.Stat(networkConfigPath) if err != nil { return nil, err } stat := f.Sys().(*syscall.Stat_t) cons, err := runtime.GetAllContainers() if err != nil { return nil, err } conf, err := libcni.ConfListFromFile(networkConfigPath) if err != nil { return nil, err } // No Bridge plugin means we bail bridge, err := genericPluginsToBridge(conf.Plugins, network.DefaultNetworkDriver) if err != nil { return nil, err } for _, outer := range bridge.IPAM.Ranges { for _, n := range outer { ipamConfig := dockerNetwork.IPAMConfig{ Subnet: n.Subnet, Gateway: n.Gateway, } ipamConfigs = append(ipamConfigs, ipamConfig) } } for _, con := range cons { data, err := con.Inspect(false) if err != nil { return nil, err } if netData, ok := data.NetworkSettings.Networks[name]; ok { containerEndpoint := types.EndpointResource{ Name: netData.NetworkID, EndpointID: netData.EndpointID, MacAddress: netData.MacAddress, IPv4Address: netData.IPAddress, IPv6Address: netData.GlobalIPv6Address, } containerEndpoints[con.ID()] = containerEndpoint } } report := types.NetworkResource{ Name: name, ID: name, Created: time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)), // nolint: unconvert Scope: "", Driver: network.DefaultNetworkDriver, EnableIPv6: false, IPAM: dockerNetwork.IPAM{ Driver: "default", Options: nil, Config: ipamConfigs, }, Internal: false, Attachable: false, Ingress: false, ConfigFrom: dockerNetwork.ConfigReference{}, ConfigOnly: false, Containers: containerEndpoints, Options: nil, Labels: nil, Peers: nil, Services: nil, } return &report, nil } func genericPluginsToBridge(plugins []*libcni.NetworkConfig, pluginType string) (network.HostLocalBridge, error) { var bridge network.HostLocalBridge generic, err := findPluginByName(plugins, pluginType) if err != nil { return bridge, err } err = json.Unmarshal(generic, &bridge) return bridge, err } func findPluginByName(plugins []*libcni.NetworkConfig, pluginType string) ([]byte, error) { for _, p := range plugins { if pluginType == p.Network.Type { return p.Bytes, nil } } return nil, errors.New("unable to find bridge plugin") } func ListNetworks(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { Filters map[string][]string `schema:"filters"` }{ // override any golang type defaults } if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } config, err := runtime.GetConfig() if err != nil { utils.InternalServerError(w, err) return } filterNames, nameFilterExists := query.Filters["name"] // TODO remove when filters are implemented if (!nameFilterExists && len(query.Filters) > 0) || len(query.Filters) > 1 { utils.InternalServerError(w, errors.New("only the name filter for listing networks is implemented")) return } netNames, err := network.GetNetworkNamesFromFileSystem(config) if err != nil { utils.InternalServerError(w, err) return } // filter by name if nameFilterExists { names := []string{} for _, name := range netNames { for _, filter := range filterNames { if strings.Contains(name, filter) { names = append(names, name) break } } } netNames = names } reports := make([]*types.NetworkResource, 0, len(netNames)) logrus.Errorf("netNames: %q", strings.Join(netNames, ", ")) for _, name := range netNames { report, err := getNetworkResourceByName(name, runtime) if err != nil { utils.InternalServerError(w, err) return } reports = append(reports, report) } utils.WriteResponse(w, http.StatusOK, reports) } func CreateNetwork(w http.ResponseWriter, r *http.Request) { var ( name string networkCreate types.NetworkCreateRequest ) runtime := r.Context().Value("runtime").(*libpod.Runtime) if err := json.NewDecoder(r.Body).Decode(&networkCreate); err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) return } if len(networkCreate.Name) > 0 { name = networkCreate.Name } if len(networkCreate.Driver) < 1 { networkCreate.Driver = network.DefaultNetworkDriver } // At present I think we should just support the bridge driver // and allow demand to make us consider more if networkCreate.Driver != network.DefaultNetworkDriver { utils.InternalServerError(w, errors.New("network create only supports the bridge driver")) return } ncOptions := entities.NetworkCreateOptions{ Driver: network.DefaultNetworkDriver, Internal: networkCreate.Internal, } if networkCreate.IPAM != nil && networkCreate.IPAM.Config != nil { if len(networkCreate.IPAM.Config) > 1 { utils.InternalServerError(w, errors.New("compat network create can only support one IPAM config")) return } if len(networkCreate.IPAM.Config[0].Subnet) > 0 { _, subnet, err := net.ParseCIDR(networkCreate.IPAM.Config[0].Subnet) if err != nil { utils.InternalServerError(w, err) return } ncOptions.Subnet = *subnet } if len(networkCreate.IPAM.Config[0].Gateway) > 0 { ncOptions.Gateway = net.ParseIP(networkCreate.IPAM.Config[0].Gateway) } if len(networkCreate.IPAM.Config[0].IPRange) > 0 { _, IPRange, err := net.ParseCIDR(networkCreate.IPAM.Config[0].IPRange) if err != nil { utils.InternalServerError(w, err) return } ncOptions.Range = *IPRange } } ce := abi.ContainerEngine{Libpod: runtime} if _, err := ce.NetworkCreate(r.Context(), name, ncOptions); err != nil { utils.InternalServerError(w, err) return } body := struct { Id string Warning []string }{ Id: name, } utils.WriteResponse(w, http.StatusCreated, body) } func RemoveNetwork(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) ic := abi.ContainerEngine{Libpod: runtime} query := struct { Force bool `schema:"force"` }{ // This is where you can override the golang default value for one of fields } decoder := r.Context().Value("decoder").(*schema.Decoder) if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } options := entities.NetworkRmOptions{ Force: query.Force, } name := utils.GetName(r) reports, err := ic.NetworkRm(r.Context(), []string{name}, options) if err != nil { utils.Error(w, "remove Network failed", http.StatusInternalServerError, err) return } if len(reports) == 0 { utils.Error(w, "remove Network failed", http.StatusInternalServerError, errors.Errorf("internal error")) return } report := reports[0] if report.Err != nil { if errors.Cause(report.Err) == define.ErrNoSuchNetwork { utils.Error(w, "network not found", http.StatusNotFound, define.ErrNoSuchNetwork) return } utils.InternalServerError(w, report.Err) return } utils.WriteResponse(w, http.StatusNoContent, "") } // Connect adds a container to a network func Connect(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) var ( aliases []string netConnect types.NetworkConnect ) if err := json.NewDecoder(r.Body).Decode(&netConnect); err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) return } name := utils.GetName(r) if netConnect.EndpointConfig != nil { if netConnect.EndpointConfig.Aliases != nil { aliases = netConnect.EndpointConfig.Aliases } } err := runtime.ConnectContainerToNetwork(netConnect.Container, name, aliases) if err != nil { if errors.Cause(err) == define.ErrNoSuchCtr { utils.ContainerNotFound(w, netConnect.Container, err) return } if errors.Cause(err) == define.ErrNoSuchNetwork { utils.Error(w, "network not found", http.StatusNotFound, err) return } utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) return } utils.WriteResponse(w, http.StatusOK, "OK") } // Disconnect removes a container from a network func Disconnect(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) var netDisconnect types.NetworkDisconnect if err := json.NewDecoder(r.Body).Decode(&netDisconnect); err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) return } name := utils.GetName(r) err := runtime.DisconnectContainerFromNetwork(netDisconnect.Container, name, netDisconnect.Force) if err != nil { if errors.Cause(err) == define.ErrNoSuchCtr { utils.Error(w, "container not found", http.StatusNotFound, err) return } if errors.Cause(err) == define.ErrNoSuchNetwork { utils.Error(w, "network not found", http.StatusNotFound, err) return } utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) return } utils.WriteResponse(w, http.StatusOK, "OK") }