Consistency, consistency, consistency: - prefix all log messages with "[net]" - important since the same process acts as both a network and ipam driver - include the API function name in all log messages - log request and response details at debug level - log request summary at info level - log ContainerStarted as a pseudo API function, but only at debug level (since it can be quite high volume) And eliminate some dot imports.
227 lines
6.6 KiB
Go
227 lines
6.6 KiB
Go
package plugin
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/docker/libnetwork/drivers/remote/api"
|
|
"github.com/docker/libnetwork/types"
|
|
|
|
"github.com/vishvananda/netlink"
|
|
weaveapi "github.com/weaveworks/weave/api"
|
|
"github.com/weaveworks/weave/common"
|
|
"github.com/weaveworks/weave/common/docker"
|
|
"github.com/weaveworks/weave/common/odp"
|
|
"github.com/weaveworks/weave/plugin/skel"
|
|
)
|
|
|
|
const (
|
|
WeaveBridge = "weave"
|
|
)
|
|
|
|
type driver struct {
|
|
scope string
|
|
noMulticastRoute bool
|
|
sync.RWMutex
|
|
endpoints map[string]struct{}
|
|
}
|
|
|
|
func New(client *docker.Client, weave *weaveapi.Client, scope string, noMulticastRoute bool) (skel.Driver, error) {
|
|
driver := &driver{
|
|
noMulticastRoute: noMulticastRoute,
|
|
scope: scope,
|
|
endpoints: make(map[string]struct{}),
|
|
}
|
|
|
|
_, err := NewWatcher(client, weave, driver)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return driver, nil
|
|
}
|
|
|
|
// === protocol handlers
|
|
|
|
func (driver *driver) GetCapabilities() (*api.GetCapabilityResponse, error) {
|
|
driver.logReq("GetCapabilities", nil, "")
|
|
var caps = &api.GetCapabilityResponse{
|
|
Scope: driver.scope,
|
|
}
|
|
driver.logRes("GetCapabilities", caps)
|
|
return caps, nil
|
|
}
|
|
|
|
func (driver *driver) CreateNetwork(create *api.CreateNetworkRequest) error {
|
|
driver.logReq("CreateNetwork", create, create.NetworkID)
|
|
return nil
|
|
}
|
|
|
|
func (driver *driver) DeleteNetwork(delete *api.DeleteNetworkRequest) error {
|
|
driver.logReq("DeleteNetwork", delete, delete.NetworkID)
|
|
return nil
|
|
}
|
|
|
|
func (driver *driver) CreateEndpoint(create *api.CreateEndpointRequest) (*api.CreateEndpointResponse, error) {
|
|
driver.logReq("CreateEndpoint", create, create.EndpointID)
|
|
endID := create.EndpointID
|
|
|
|
if create.Interface == nil {
|
|
return nil, driver.error("CreateEndpoint", "Not supported: creating an interface from within CreateEndpoint")
|
|
}
|
|
driver.Lock()
|
|
driver.endpoints[endID] = struct{}{}
|
|
driver.Unlock()
|
|
resp := &api.CreateEndpointResponse{}
|
|
|
|
driver.logRes("CreateEndpoint", resp)
|
|
return resp, nil
|
|
}
|
|
|
|
func (driver *driver) DeleteEndpoint(deleteReq *api.DeleteEndpointRequest) error {
|
|
driver.logReq("DeleteEndpoint", deleteReq, deleteReq.EndpointID)
|
|
driver.Lock()
|
|
delete(driver.endpoints, deleteReq.EndpointID)
|
|
driver.Unlock()
|
|
return nil
|
|
}
|
|
|
|
func (driver *driver) HasEndpoint(endpointID string) bool {
|
|
driver.Lock()
|
|
_, found := driver.endpoints[endpointID]
|
|
driver.Unlock()
|
|
return found
|
|
}
|
|
|
|
func (driver *driver) EndpointInfo(req *api.EndpointInfoRequest) (*api.EndpointInfoResponse, error) {
|
|
driver.logReq("EndpointInfo", req, req.EndpointID)
|
|
return &api.EndpointInfoResponse{Value: map[string]interface{}{}}, nil
|
|
}
|
|
|
|
func (driver *driver) JoinEndpoint(j *api.JoinRequest) (*api.JoinResponse, error) {
|
|
driver.logReq("JoinEndpoint", j, fmt.Sprintf("%s:%s to %s", j.NetworkID, j.EndpointID, j.SandboxKey))
|
|
|
|
local, err := createAndAttach(j.EndpointID, WeaveBridge, 0)
|
|
if err != nil {
|
|
return nil, driver.error("JoinEndpoint", "%s", err)
|
|
}
|
|
|
|
if err := netlink.LinkSetUp(local); err != nil {
|
|
return nil, driver.error("JoinEndpoint", "unable to bring up veth %s-%s: %s", local.Name, local.PeerName, err)
|
|
}
|
|
|
|
ifname := &api.InterfaceName{
|
|
SrcName: local.PeerName,
|
|
DstPrefix: "ethwe",
|
|
}
|
|
|
|
response := &api.JoinResponse{
|
|
InterfaceName: ifname,
|
|
}
|
|
if !driver.noMulticastRoute {
|
|
multicastRoute := api.StaticRoute{
|
|
Destination: "224.0.0.0/4",
|
|
RouteType: types.CONNECTED,
|
|
}
|
|
response.StaticRoutes = append(response.StaticRoutes, multicastRoute)
|
|
}
|
|
driver.logRes("JoinEndpoint", response)
|
|
return response, nil
|
|
}
|
|
|
|
// create and attach local name to the Weave bridge
|
|
func createAndAttach(id, bridgeName string, mtu int) (*netlink.Veth, error) {
|
|
maybeBridge, err := netlink.LinkByName(bridgeName)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(`bridge "%s" not present; did you launch weave?`, bridgeName)
|
|
}
|
|
|
|
local := vethPair(id[:5])
|
|
if mtu == 0 {
|
|
local.Attrs().MTU = maybeBridge.Attrs().MTU
|
|
} else {
|
|
local.Attrs().MTU = mtu
|
|
}
|
|
if err := netlink.LinkAdd(local); err != nil {
|
|
return nil, fmt.Errorf(`could not create veth pair %s-%s: %s`, local.Name, local.PeerName, err)
|
|
}
|
|
|
|
switch maybeBridge.(type) {
|
|
case *netlink.Bridge:
|
|
if err := netlink.LinkSetMasterByIndex(local, maybeBridge.Attrs().Index); err != nil {
|
|
return nil, fmt.Errorf(`unable to set master of %s: %s`, local.Name, err)
|
|
}
|
|
case *netlink.GenericLink:
|
|
if maybeBridge.Type() != "openvswitch" {
|
|
return nil, fmt.Errorf(`device "%s" is of type "%s"`, bridgeName, maybeBridge.Type())
|
|
}
|
|
if err := odp.AddDatapathInterface(bridgeName, local.Name); err != nil {
|
|
return nil, fmt.Errorf(`failed to attach %s to device "%s": %s`, local.Name, bridgeName, err)
|
|
}
|
|
case *netlink.Device:
|
|
// Assume it's our openvswitch device, and the kernel has not been updated to report the kind.
|
|
if err := odp.AddDatapathInterface(bridgeName, local.Name); err != nil {
|
|
return nil, fmt.Errorf(`failed to attach %s to device "%s": %s`, local.Name, bridgeName, err)
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf(`device "%s" is not a bridge`, bridgeName)
|
|
}
|
|
return local, nil
|
|
}
|
|
|
|
func (driver *driver) LeaveEndpoint(leave *api.LeaveRequest) error {
|
|
driver.logReq("LeaveEndpoint", leave, fmt.Sprintf("%s:%s", leave.NetworkID, leave.EndpointID))
|
|
|
|
local := vethPair(leave.EndpointID[:5])
|
|
if err := netlink.LinkDel(local); err != nil {
|
|
driver.warn("LeaveEndpoint", "unable to delete veth: %s", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (driver *driver) DiscoverNew(disco *api.DiscoveryNotification) error {
|
|
driver.logReq("DiscoverNew", disco, "")
|
|
return nil
|
|
}
|
|
|
|
func (driver *driver) DiscoverDelete(disco *api.DiscoveryNotification) error {
|
|
driver.logReq("DiscoverDelete", disco, "")
|
|
return nil
|
|
}
|
|
|
|
// ===
|
|
|
|
func vethPair(suffix string) *netlink.Veth {
|
|
return &netlink.Veth{
|
|
LinkAttrs: netlink.LinkAttrs{Name: "vethwl" + suffix},
|
|
PeerName: "vethwg" + suffix,
|
|
}
|
|
}
|
|
|
|
// logging
|
|
|
|
func (driver *driver) logReq(fun string, req interface{}, short string) {
|
|
driver.log(common.Log.Debugf, " %+v", fun, req)
|
|
common.Log.Infof("[net] %s %s", fun, short)
|
|
}
|
|
|
|
func (driver *driver) logRes(fun string, res interface{}) {
|
|
driver.log(common.Log.Debugf, " %+v", fun, res)
|
|
}
|
|
|
|
func (driver *driver) warn(fun string, format string, a ...interface{}) {
|
|
driver.log(common.Log.Warnf, ": "+format, fun, a...)
|
|
}
|
|
|
|
func (driver *driver) debug(fun string, format string, a ...interface{}) {
|
|
driver.log(common.Log.Debugf, ": "+format, fun, a...)
|
|
}
|
|
|
|
func (driver *driver) error(fun string, format string, a ...interface{}) error {
|
|
driver.log(common.Log.Errorf, ": "+format, fun, a...)
|
|
return fmt.Errorf(format, a...)
|
|
}
|
|
|
|
func (driver *driver) log(f func(string, ...interface{}), format string, fun string, a ...interface{}) {
|
|
f("[net] %s"+format, append([]interface{}{fun}, a...)...)
|
|
}
|