From f943e5d91f6922c599b46727e2dfd5e8e7e5bea8 Mon Sep 17 00:00:00 2001
From: jiangpengfei <jiangpengfei9@huawei.com>
Date: Wed, 19 Aug 2020 22:43:54 +0800
Subject: [PATCH 50/50] runtime: add kata-network upate-iface subcommand

reason: add kata-network update-iface subcommand to
update the exist interface in the Kata VM.

Signed-off-by: jiangpengfei <jiangpengfei9@huawei.com>
---
 cli/network.go                     |  37 +++++++++-
 virtcontainers/api.go              |  25 +++++--
 virtcontainers/implementation.go   |   5 ++
 virtcontainers/interfaces.go       |   1 +
 virtcontainers/pkg/vcmock/mock.go  |   8 +++
 virtcontainers/pkg/vcmock/types.go |   1 +
 virtcontainers/sandbox.go          | 137 +++++++++++++++++++++++++++++++++++++
 7 files changed, 207 insertions(+), 7 deletions(-)

diff --git a/cli/network.go b/cli/network.go
index 3dd0971e..7dce0528 100644
--- a/cli/network.go
+++ b/cli/network.go
@@ -65,8 +65,9 @@ var addIfaceCommand = cli.Command{
 		   "hwAddr":"<mac>",
 		   "linkType":"tap",
 		   "vhostUserSocket":"<path>"
+           "queues":<queues>
        }
-       device,name,mtu,hwAddr are required, IPAddresses and vhostUserSocket are optional.
+       device,name,mtu,hwAddr are required, IPAddresses, queues and vhostUserSocket are optional.
 	`,
 	Flags: []cli.Flag{},
 	Action: func(context *cli.Context) error {
@@ -104,6 +105,34 @@ var delIfaceCommand = cli.Command{
 	},
 }
 
+var updateIfaceCommand = cli.Command{
+	Name:  "update-iface",
+	Usage: "update the interface in container",
+	ArgsUsage: `update-iface <container-id> file or - for stdin
+    file or stdin for example:
+	{
+		"device":"",
+		"name":"<interface-name>",
+		"IPAddresses":[{"address":"<ip>","mask":"<mask>"}],
+		"mtu":0,
+		"hwAddr":"",
+		"linkType":"",
+		"vhostUserSocket":"",
+		"queues":0
+	}
+	name is required,IPAddresses is optional,device,mtu,hwAddr,linkType,queues and vhostUserSocket should be 0 or empty.
+	`,
+	Flags: []cli.Flag{},
+	Action: func(context *cli.Context) error {
+		ctx, err := cliContextToContext(context)
+		if err != nil {
+			return err
+		}
+
+		return networkModifyCommand(ctx, context.Args().First(), context.Args().Get(1), interfaceType, vcTypes.NetworkOpUpdate)
+	},
+}
+
 var listIfacesCommand = cli.Command{
 	Name:      "list-ifaces",
 	Usage:     "list network interfaces in a container",
@@ -262,6 +291,12 @@ func networkModifyCommand(ctx context.Context, containerID, input string, opType
 				kataLog.WithField("resulting-interface", fmt.Sprintf("%+v", resultingInf)).
 					WithError(err).Error("delete interface failed")
 			}
+		case vcTypes.NetworkOpUpdate:
+			resultingInf, err = vci.UpdateInterface(ctx, sandboxID, inf)
+			if err != nil {
+				kataLog.WithField("resulting-interface", fmt.Sprintf("%+v", resultingInf)).
+					WithError(err).Error("update interface failed")
+			}
 		}
 
 		json.NewEncoder(output).Encode(resultingInf)
diff --git a/virtcontainers/api.go b/virtcontainers/api.go
index 7036df8c..ca5412a9 100644
--- a/virtcontainers/api.go
+++ b/virtcontainers/api.go
@@ -889,7 +889,7 @@ func AddDevice(ctx context.Context, sandboxID string, info deviceConfig.DeviceIn
 	return s.AddDevice(info)
 }
 
-func toggleInterface(ctx context.Context, sandboxID string, inf *vcTypes.Interface, add bool) (*vcTypes.Interface, error) {
+func toggleInterface(ctx context.Context, sandboxID string, inf *vcTypes.Interface, op vcTypes.NetworkOp) (*vcTypes.Interface, error) {
 	if sandboxID == "" {
 		return nil, vcTypes.ErrNeedSandboxID
 	}
@@ -906,11 +906,16 @@ func toggleInterface(ctx context.Context, sandboxID string, inf *vcTypes.Interfa
 	}
 	defer s.releaseStatelessSandbox()
 
-	if add {
+	switch op {
+	case vcTypes.NetworkOpAdd:
 		return s.AddInterface(inf)
+	case vcTypes.NetworkOpRemove:
+		return s.RemoveInterface(inf)
+	case vcTypes.NetworkOpUpdate:
+		return s.UpdateInterface(inf)
+	default:
+		return nil, fmt.Errorf("operation is not found")
 	}
-
-	return s.RemoveInterface(inf)
 }
 
 // AddInterface is the virtcontainers add interface entry point.
@@ -918,7 +923,7 @@ func AddInterface(ctx context.Context, sandboxID string, inf *vcTypes.Interface)
 	span, ctx := trace(ctx, "AddInterface")
 	defer span.Finish()
 
-	return toggleInterface(ctx, sandboxID, inf, true)
+	return toggleInterface(ctx, sandboxID, inf, vcTypes.NetworkOpAdd)
 }
 
 // RemoveInterface is the virtcontainers remove interface entry point.
@@ -926,7 +931,15 @@ func RemoveInterface(ctx context.Context, sandboxID string, inf *vcTypes.Interfa
 	span, ctx := trace(ctx, "RemoveInterface")
 	defer span.Finish()
 
-	return toggleInterface(ctx, sandboxID, inf, false)
+	return toggleInterface(ctx, sandboxID, inf, vcTypes.NetworkOpRemove)
+}
+
+// UpdateInterface updates interface entry point in the virtcontainers.
+func UpdateInterface(ctx context.Context, sandboxID string, inf *vcTypes.Interface) (*vcTypes.Interface, error) {
+	span, ctx := trace(ctx, "UpdateInterface")
+	defer span.Finish()
+
+	return toggleInterface(ctx, sandboxID, inf, vcTypes.NetworkOpUpdate)
 }
 
 // ListInterfaces is the virtcontainers list interfaces entry point.
diff --git a/virtcontainers/implementation.go b/virtcontainers/implementation.go
index 0265d1ed..e4bc4ae6 100644
--- a/virtcontainers/implementation.go
+++ b/virtcontainers/implementation.go
@@ -163,6 +163,11 @@ func (impl *VCImpl) ListInterfaces(ctx context.Context, sandboxID string) ([]*vc
 	return ListInterfaces(ctx, sandboxID)
 }
 
+// ListInterfaces implements the VC function of the same name.
+func (impl *VCImpl) UpdateInterface(ctx context.Context, sandboxID string, inf *vcTypes.Interface) (*vcTypes.Interface, error) {
+	return UpdateInterface(ctx, sandboxID, inf)
+}
+
 // UpdateRoutes implements the VC function of the same name.
 func (impl *VCImpl) UpdateRoutes(ctx context.Context, sandboxID string, routes []*vcTypes.Route, op vcTypes.NetworkOp) ([]*vcTypes.Route, error) {
 	return UpdateRoutes(ctx, sandboxID, routes, op)
diff --git a/virtcontainers/interfaces.go b/virtcontainers/interfaces.go
index d8f0be69..0fd12d84 100644
--- a/virtcontainers/interfaces.go
+++ b/virtcontainers/interfaces.go
@@ -51,6 +51,7 @@ type VC interface {
 
 	AddInterface(ctx context.Context, sandboxID string, inf *vcTypes.Interface) (*vcTypes.Interface, error)
 	RemoveInterface(ctx context.Context, sandboxID string, inf *vcTypes.Interface) (*vcTypes.Interface, error)
+	UpdateInterface(ctx context.Context, sandboxID string, inf *vcTypes.Interface) (*vcTypes.Interface, error)
 	ListInterfaces(ctx context.Context, sandboxID string) ([]*vcTypes.Interface, error)
 	UpdateRoutes(ctx context.Context, sandboxID string, routes []*vcTypes.Route, op vcTypes.NetworkOp) ([]*vcTypes.Route, error)
 	ListRoutes(ctx context.Context, sandboxID string) ([]*vcTypes.Route, error)
diff --git a/virtcontainers/pkg/vcmock/mock.go b/virtcontainers/pkg/vcmock/mock.go
index aa9bae9a..d3bf201f 100644
--- a/virtcontainers/pkg/vcmock/mock.go
+++ b/virtcontainers/pkg/vcmock/mock.go
@@ -273,6 +273,14 @@ func (m *VCMock) ListInterfaces(ctx context.Context, sandboxID string) ([]*vcTyp
 	return nil, fmt.Errorf("%s: %s (%+v): sandboxID: %v", mockErrorPrefix, getSelf(), m, sandboxID)
 }
 
+// UpdateInterface implements the VC function of the same name
+func (m *VCMock) UpdateInterface(ctx context.Context, sandboxID string, inf *vcTypes.Interface) (*vcTypes.Interface, error) {
+	if m.UpdateRoutesFunc != nil {
+		return m.UpdateInterfaceFunc(ctx, sandboxID, inf)
+	}
+	return nil, fmt.Errorf("%s: %s (%+v): sandboxID: %v", mockErrorPrefix, getSelf(), m, sandboxID)
+}
+
 // UpdateRoutes implements the VC function of the same name.
 func (m *VCMock) UpdateRoutes(ctx context.Context, sandboxID string, routes []*vcTypes.Route, op vcTypes.NetworkOp) ([]*vcTypes.Route, error) {
 	if m.UpdateRoutesFunc != nil {
diff --git a/virtcontainers/pkg/vcmock/types.go b/virtcontainers/pkg/vcmock/types.go
index 610b4602..152e66b7 100644
--- a/virtcontainers/pkg/vcmock/types.go
+++ b/virtcontainers/pkg/vcmock/types.go
@@ -73,6 +73,7 @@ type VCMock struct {
 	AddInterfaceFunc     func(ctx context.Context, sandboxID string, inf *vcTypes.Interface) (*vcTypes.Interface, error)
 	RemoveInterfaceFunc  func(ctx context.Context, sandboxID string, inf *vcTypes.Interface) (*vcTypes.Interface, error)
 	ListInterfacesFunc   func(ctx context.Context, sandboxID string) ([]*vcTypes.Interface, error)
+	UpdateInterfaceFunc  func(ctx context.Context, sandboxID string, inf *vcTypes.Interface) (*vcTypes.Interface, error)
 	UpdateRoutesFunc     func(ctx context.Context, sandboxID string, routes []*vcTypes.Route) ([]*vcTypes.Route, error)
 	ListRoutesFunc       func(ctx context.Context, sandboxID string) ([]*vcTypes.Route, error)
 	CleanupContainerFunc func(ctx context.Context, sandboxID, containerID string, force bool) error
diff --git a/virtcontainers/sandbox.go b/virtcontainers/sandbox.go
index d8ab6c1a..174e6cb6 100644
--- a/virtcontainers/sandbox.go
+++ b/virtcontainers/sandbox.go
@@ -1031,6 +1031,54 @@ func (s *Sandbox) RemoveInterface(inf *vcTypes.Interface) (*vcTypes.Interface, e
 	return nil, nil
 }
 
+// UpdateInterface updates the nic.
+func (s *Sandbox) UpdateInterface(inf *vcTypes.Interface) (*vcTypes.Interface, error) {
+	err := checkUpdateInterfaceInfo(inf)
+	if err != nil {
+		return nil, err
+	}
+
+	nicInGuest := false
+
+	endpoint := s.getEndpointByName(inf.Name)
+	// nic is not in local endpoints
+	if endpoint == nil {
+		interfaceInGuestInf := s.getInterfaceInfByName(inf.Name)
+		if interfaceInGuestInf == nil {
+			return nil, fmt.Errorf("can not find the network interface")
+		}
+
+		nicInGuest = true
+		endpoint, err = s.generateEndpointForStore(interfaceInGuestInf)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	// update endpoint ip
+	err = s.updateEndpointIP(endpoint, inf)
+	if err != nil {
+		return nil, err
+	}
+
+	s.Logger().WithField("endpoint-type", endpoint.Type()).Info("Update endpoint")
+	grpcIf, err := s.agent.updateInterface(inf)
+	if err != nil {
+		return nil, err
+	}
+
+	if nicInGuest {
+		s.networkNS.Endpoints = append(s.networkNS.Endpoints, endpoint)
+	}
+
+	if err = s.Save(); err != nil {
+		s.Logger().WithError(err).Error("failed to store the network information")
+		return nil, err
+	}
+
+	return grpcIf, nil
+}
+
 // ListInterfaces lists all nics and their configurations in the sandbox.
 func (s *Sandbox) ListInterfaces() ([]*vcTypes.Interface, error) {
 	return s.agent.listInterfaces()
@@ -2431,6 +2479,95 @@ func (s *Sandbox) IsCompatOldCNI() bool {
 	return s.config.NetworkConfig.EnableCompatOldCNI
 }
 
+func checkUpdateInterfaceInfo(inf *vcTypes.Interface) error {
+	if inf.Mtu > 0 || inf.Queues > 0 || inf.Device != "" || inf.VhostUserSocket != "" || inf.LinkType != "" || inf.HwAddr != "" {
+		return fmt.Errorf("device,mtu,hwAddr,linkType,queues and vhostUserSocket should be 0 or empty")
+	}
+
+	return nil
+}
+
+// getEndpointByName returns the endpoint by name.
+func (s *Sandbox) getEndpointByName(name string) Endpoint {
+	if name == "" {
+		return nil
+	}
+
+	for i, endpoint := range s.networkNS.Endpoints {
+		if endpoint.Name() == name {
+			return s.networkNS.Endpoints[i]
+		}
+	}
+
+	return nil
+}
+
+// getInterfaceInfByName returns the interface information.
+func (s *Sandbox) getInterfaceInfByName(name string) *vcTypes.Interface {
+	if name == "" {
+		return nil
+	}
+
+	nics, err := s.agent.listInterfaces()
+	if err != nil {
+		return nil
+	}
+
+	for _, i := range nics {
+		if i.Name == name {
+			return i
+		}
+	}
+
+	return nil
+}
+
+// generateEndpointForStore only generates endpoint information for store,it does not create a endpoint
+func (s *Sandbox) generateEndpointForStore(inf *vcTypes.Interface) (*TapEndpoint, error) {
+	netInfo, err := s.generateNetInfo(inf)
+	if err != nil {
+		return nil, err
+	}
+
+	endpoint := &TapEndpoint{
+		TapInterface: TapInterface{
+			Name: inf.Name,
+			TAPIface: NetworkInterface{
+				HardAddr: inf.HwAddr,
+			},
+		},
+		EndpointType: TapEndpointType,
+	}
+
+	endpoint.SetProperties(netInfo)
+
+	return endpoint, nil
+}
+
+func (s *Sandbox) updateEndpointIP(endpoint Endpoint, inf *vcTypes.Interface) error {
+	if endpoint == nil {
+		return fmt.Errorf("endpoint is nil")
+	}
+
+	netInfo := endpoint.Properties()
+
+	var addrs []netlink.Addr
+	for _, addr := range inf.IPAddresses {
+		netlinkAddrStr := fmt.Sprintf("%s/%s", addr.Address, addr.Mask)
+		netlinkAddr, err := netlink.ParseAddr(netlinkAddrStr)
+		if err != nil {
+			return fmt.Errorf("could not parse %q: %v", netlinkAddrStr, err)
+		}
+
+		addrs = append(addrs, *netlinkAddr)
+	}
+
+	netInfo.Addrs = addrs
+	endpoint.SetProperties(netInfo)
+
+	return nil
+}
+
 // updateStaticSandboxResources update sandbox's cpu and memory resource passed by
 // sandbox_cpu and sandbox_mem annotations
 func updateStaticSandboxResources(sandboxConfig *SandboxConfig) error {
-- 
2.14.3 (Apple Git-98)

