virtcontainers: Unify Network endpoints management interface

And only have AddEndpoints/RemoveEndpoints for all cases (single
endpoint vs all of them, hotplug or not).

Signed-off-by: Samuel Ortiz <s.ortiz@apple.com>
This commit is contained in:
Samuel Ortiz 2021-11-09 04:19:10 +00:00 committed by Samuel Ortiz
parent c67109a251
commit 7fca5792f7
4 changed files with 71 additions and 38 deletions

View File

@ -228,3 +228,13 @@ func loadTuntapIf(tuntapif *persistapi.TuntapInterface) *TuntapInterface {
}, },
} }
} }
func findEndpoint(e Endpoint, endpoints []Endpoint) (Endpoint, int) {
for idx, ep := range endpoints {
if ep.HardwareAddr() == e.HardwareAddr() {
return ep, idx
}
}
return nil, 0
}

View File

@ -97,6 +97,7 @@ type NetlinkIface struct {
type NetworkInfo struct { type NetworkInfo struct {
Iface NetlinkIface Iface NetlinkIface
DNS DNSInfo DNS DNSInfo
Link netlink.Link
Addrs []netlink.Addr Addrs []netlink.Addr
Routes []netlink.Route Routes []netlink.Route
Neighbors []netlink.Neigh Neighbors []netlink.Neigh
@ -197,19 +198,16 @@ type NetworkConfig struct {
} }
type Network interface { type Network interface {
// Add adds all needed networking endpoints to a sandbox's network // AddEndpoint adds endpoints to a sandbox's network.
Add(context.Context, *Sandbox, bool) error // If the NetworkInfo slice is empty, implementations are expected to scan
// the sandbox's network for all existing endpoints.
AddEndpoints(context.Context, *Sandbox, []NetworkInfo, bool) ([]Endpoint, error)
// AddEndpoint adds one single endpoint to a sandbox's network. // RemoveEndpoints removes endpoints from the sandbox's network.
AddEndpoint(context.Context, *Sandbox, NetworkInfo, netlink.Link, bool) (Endpoint, error) // If the the endpoint slice is empty, all endpoints will be removed.
// Remove removes all the networking endpoints from a sandbox's network.
// If the network has been created by virtcontainers, Remove also deletes // If the network has been created by virtcontainers, Remove also deletes
// the network. // the network.
Remove(context.Context) error RemoveEndpoints(context.Context, *Sandbox, []Endpoint, bool) error
// RemoveEndpoint removes one single endpoint from the sandbox's network.
RemoveEndpoint(context.Context, *Sandbox, int, bool) error
// Run runs a callback in a sandbox's network. // Run runs a callback in a sandbox's network.
Run(context.Context, func() error) error Run(context.Context, func() error) error

View File

@ -107,7 +107,7 @@ func (n *LinuxNetwork) trace(ctx context.Context, name string) (otelTrace.Span,
return networkTrace(ctx, name, nil) return networkTrace(ctx, name, nil)
} }
func (n *LinuxNetwork) AddEndpoint(ctx context.Context, s *Sandbox, netInfo NetworkInfo, link netlink.Link, hotplug bool) (Endpoint, error) { func (n *LinuxNetwork) addSingleEndpoint(ctx context.Context, s *Sandbox, netInfo NetworkInfo, hotplug bool) (Endpoint, error) {
var endpoint Endpoint var endpoint Endpoint
// TODO: This is the incoming interface // TODO: This is the incoming interface
// based on the incoming interface we should create // based on the incoming interface we should create
@ -147,8 +147,8 @@ func (n *LinuxNetwork) AddEndpoint(ctx context.Context, s *Sandbox, netInfo Netw
networkLogger().Info("tap interface found") networkLogger().Info("tap interface found")
endpoint, err = createTapNetworkEndpoint(idx, netInfo.Iface.Name) endpoint, err = createTapNetworkEndpoint(idx, netInfo.Iface.Name)
} else if netInfo.Iface.Type == "tuntap" { } else if netInfo.Iface.Type == "tuntap" {
if link != nil { if netInfo.Link != nil {
switch link.(*netlink.Tuntap).Mode { switch netInfo.Link.(*netlink.Tuntap).Mode {
case 0: case 0:
// mount /sys/class/net to get links // mount /sys/class/net to get links
return nil, fmt.Errorf("Network device mode not determined correctly. Mount sysfs in caller") return nil, fmt.Errorf("Network device mode not determined correctly. Mount sysfs in caller")
@ -158,7 +158,7 @@ func (n *LinuxNetwork) AddEndpoint(ctx context.Context, s *Sandbox, netInfo Netw
networkLogger().Info("tuntap tap interface found") networkLogger().Info("tuntap tap interface found")
endpoint, err = createTuntapNetworkEndpoint(idx, netInfo.Iface.Name, netInfo.Iface.HardwareAddr, n.interworkingModel) endpoint, err = createTuntapNetworkEndpoint(idx, netInfo.Iface.Name, netInfo.Iface.HardwareAddr, n.interworkingModel)
default: default:
return nil, fmt.Errorf("tuntap network %v mode unsupported", link.(*netlink.Tuntap).Mode) return nil, fmt.Errorf("tuntap network %v mode unsupported", netInfo.Link.(*netlink.Tuntap).Mode)
} }
} }
} else if netInfo.Iface.Type == "veth" { } else if netInfo.Iface.Type == "veth" {
@ -217,7 +217,7 @@ func (n *LinuxNetwork) AddEndpoint(ctx context.Context, s *Sandbox, netInfo Netw
return endpoint, nil return endpoint, nil
} }
func (n *LinuxNetwork) RemoveEndpoint(ctx context.Context, s *Sandbox, idx int, hotplug bool) error { func (n *LinuxNetwork) removeSingleEndpoint(ctx context.Context, s *Sandbox, idx int, hotplug bool) error {
if idx > len(n.eps)-1 { if idx > len(n.eps)-1 {
return fmt.Errorf("Endpoint index overflow") return fmt.Errorf("Endpoint index overflow")
} }
@ -261,7 +261,7 @@ func (n *LinuxNetwork) RemoveEndpoint(ctx context.Context, s *Sandbox, idx int,
// Scan the networking namespace through netlink and then: // Scan the networking namespace through netlink and then:
// 1. Create the endpoints for the relevant interfaces found there. // 1. Create the endpoints for the relevant interfaces found there.
// 2. Attach them to the VM. // 2. Attach them to the VM.
func (n *LinuxNetwork) attachEndpoints(ctx context.Context, s *Sandbox, hotplug bool) error { func (n *LinuxNetwork) addAllEndpoints(ctx context.Context, s *Sandbox, hotplug bool) error {
netnsHandle, err := netns.GetFromPath(n.netNSPath) netnsHandle, err := netns.GetFromPath(n.netNSPath)
if err != nil { if err != nil {
return err return err
@ -298,7 +298,7 @@ func (n *LinuxNetwork) attachEndpoints(ctx context.Context, s *Sandbox, hotplug
continue continue
} }
_, err = n.AddEndpoint(ctx, s, netInfo, link, hotplug) _, err = n.addSingleEndpoint(ctx, s, netInfo, hotplug)
if err != nil { if err != nil {
return err return err
} }
@ -324,36 +324,57 @@ func (n *LinuxNetwork) Run(ctx context.Context, cb func() error) error {
} }
// Add adds all needed interfaces inside the network namespace. // Add adds all needed interfaces inside the network namespace.
func (n *LinuxNetwork) Add(ctx context.Context, s *Sandbox, hotplug bool) error { func (n *LinuxNetwork) AddEndpoints(ctx context.Context, s *Sandbox, endpointsInfo []NetworkInfo, hotplug bool) ([]Endpoint, error) {
span, ctx := n.trace(ctx, "Add") span, ctx := n.trace(ctx, "AddEndpoints")
katatrace.AddTags(span, "type", n.interworkingModel.GetModel()) katatrace.AddTags(span, "type", n.interworkingModel.GetModel())
defer span.End() defer span.End()
if err := n.attachEndpoints(ctx, s, hotplug); err != nil { if endpointsInfo == nil {
return err if err := n.addAllEndpoints(ctx, s, hotplug); err != nil {
return nil, err
}
} else {
for _, ep := range endpointsInfo {
if _, err := n.addSingleEndpoint(ctx, s, ep, hotplug); err != nil {
n.eps = nil
return nil, err
}
}
} }
katatrace.AddTags(span, "endpoints", n.eps, "hotplug", hotplug) katatrace.AddTags(span, "endpoints", n.eps, "hotplug", hotplug)
networkLogger().Debug("Network added") networkLogger().Debug("Endpoints added")
return nil return n.eps, nil
} }
// Remove network endpoints in the network namespace. It also deletes the network // Remove network endpoints in the network namespace. It also deletes the network
// namespace in case the namespace has been created by us. // namespace in case the namespace has been created by us.
func (n *LinuxNetwork) Remove(ctx context.Context) error { func (n *LinuxNetwork) RemoveEndpoints(ctx context.Context, s *Sandbox, endpoints []Endpoint, hotplug bool) error {
span, ctx := n.trace(ctx, "Remove") span, ctx := n.trace(ctx, "RemoveEndpoints")
defer span.End() defer span.End()
for i := range n.eps { eps := n.eps
if err := n.RemoveEndpoint(ctx, nil, i, false); err != nil { if endpoints != nil {
eps = endpoints
}
for idx, ep := range eps {
if endpoints != nil {
new_ep, _ := findEndpoint(ep, n.eps)
if new_ep == nil {
continue
}
}
if err := n.removeSingleEndpoint(ctx, s, idx, hotplug); err != nil {
return err return err
} }
} }
networkLogger().Debug("Network removed") networkLogger().Debug("Endpoints removed")
if n.netNSCreated { if n.netNSCreated && endpoints == nil {
networkLogger().Infof("Network namespace %q deleted", n.netNSPath) networkLogger().Infof("Network namespace %q deleted", n.netNSPath)
return deleteNetNS(n.netNSPath) return deleteNetNS(n.netNSPath)
} }
@ -1056,6 +1077,7 @@ func networkInfoFromLink(handle *netlink.Handle, link netlink.Link) (NetworkInfo
Addrs: addrs, Addrs: addrs,
Routes: routes, Routes: routes,
Neighbors: neighbors, Neighbors: neighbors,
Link: link,
}, nil }, nil
} }

View File

@ -818,7 +818,7 @@ func (s *Sandbox) createNetwork(ctx context.Context) error {
// after vm is started. // after vm is started.
if s.factory == nil { if s.factory == nil {
// Add the network // Add the network
if err := s.network.Add(ctx, s, false); err != nil { if _, err := s.network.AddEndpoints(ctx, s, nil, false); err != nil {
return err return err
} }
} }
@ -853,7 +853,7 @@ func (s *Sandbox) removeNetwork(ctx context.Context) error {
span, ctx := katatrace.Trace(ctx, s.Logger(), "removeNetwork", sandboxTracingTags, map[string]string{"sandbox_id": s.id}) span, ctx := katatrace.Trace(ctx, s.Logger(), "removeNetwork", sandboxTracingTags, map[string]string{"sandbox_id": s.id})
defer span.End() defer span.End()
return s.network.Remove(ctx) return s.network.RemoveEndpoints(ctx, s, nil, false)
} }
func (s *Sandbox) generateNetInfo(inf *pbTypes.Interface) (NetworkInfo, error) { func (s *Sandbox) generateNetInfo(inf *pbTypes.Interface) (NetworkInfo, error) {
@ -893,21 +893,24 @@ func (s *Sandbox) AddInterface(ctx context.Context, inf *pbTypes.Interface) (*pb
return nil, err return nil, err
} }
endpoint, err := s.network.AddEndpoint(ctx, s, netInfo, nil, true) endpoints, err := s.network.AddEndpoints(ctx, s, []NetworkInfo{netInfo}, true)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer func() { defer func() {
if err != nil { if err != nil {
if errDetach := s.network.RemoveEndpoint(ctx, s, len(s.network.Endpoints())-1, true); err != nil { eps := s.network.Endpoints()
s.Logger().WithField("endpoint-type", endpoint.Type()).WithError(errDetach).Error("rollback hot attaching endpoint failed") // The newly added endpoint is last.
added_ep := eps[len(eps)-1]
if errDetach := s.network.RemoveEndpoints(ctx, s, []Endpoint{added_ep}, true); err != nil {
s.Logger().WithField("endpoint-type", added_ep.Type()).WithError(errDetach).Error("rollback hot attaching endpoint failed")
} }
} }
}() }()
// Add network for vm // Add network for vm
inf.PciPath = endpoint.PciPath().String() inf.PciPath = endpoints[0].PciPath().String()
result, err := s.agent.updateInterface(ctx, inf) result, err := s.agent.updateInterface(ctx, inf)
if err != nil { if err != nil {
return nil, err return nil, err
@ -923,10 +926,10 @@ func (s *Sandbox) AddInterface(ctx context.Context, inf *pbTypes.Interface) (*pb
// RemoveInterface removes a nic of the sandbox. // RemoveInterface removes a nic of the sandbox.
func (s *Sandbox) RemoveInterface(ctx context.Context, inf *pbTypes.Interface) (*pbTypes.Interface, error) { func (s *Sandbox) RemoveInterface(ctx context.Context, inf *pbTypes.Interface) (*pbTypes.Interface, error) {
for i, endpoint := range s.network.Endpoints() { for _, endpoint := range s.network.Endpoints() {
if endpoint.HardwareAddr() == inf.HwAddr { if endpoint.HardwareAddr() == inf.HwAddr {
s.Logger().WithField("endpoint-type", endpoint.Type()).Info("Hot detaching endpoint") s.Logger().WithField("endpoint-type", endpoint.Type()).Info("Hot detaching endpoint")
if err := s.network.RemoveEndpoint(ctx, s, i, true); err != nil { if err := s.network.RemoveEndpoints(ctx, s, []Endpoint{endpoint}, true); err != nil {
return inf, err return inf, err
} }
@ -1205,7 +1208,7 @@ func (s *Sandbox) startVM(ctx context.Context) (err error) {
// In case of vm factory, network interfaces are hotplugged // In case of vm factory, network interfaces are hotplugged
// after vm is started. // after vm is started.
if s.factory != nil { if s.factory != nil {
if err := s.network.Add(ctx, s, true); err != nil { if _, err := s.network.AddEndpoints(ctx, s, nil, true); err != nil {
return err return err
} }
} }