go.ligato.io/vpp-agent/v3@v3.5.0/examples/localclient_vpp/plugins/main.go (about)

     1  // Copyright (c) 2017 Cisco and/or its affiliates.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at:
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package main
    16  
    17  import (
    18  	"context"
    19  	"os"
    20  	"sync"
    21  	"time"
    22  
    23  	"log"
    24  
    25  	"go.ligato.io/cn-infra/v2/agent"
    26  	"go.ligato.io/cn-infra/v2/logging"
    27  	"go.ligato.io/cn-infra/v2/logging/logrus"
    28  
    29  	"go.ligato.io/vpp-agent/v3/clientv2/vpp/localclient"
    30  	"go.ligato.io/vpp-agent/v3/cmd/vpp-agent/app"
    31  	"go.ligato.io/vpp-agent/v3/plugins/orchestrator"
    32  	acl "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/acl"
    33  	interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces"
    34  	l2 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/l2"
    35  	l3 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/l3"
    36  )
    37  
    38  // init sets the default logging level.
    39  func init() {
    40  	logrus.DefaultLogger().SetOutput(os.Stdout)
    41  	logrus.DefaultLogger().SetLevel(logging.DebugLevel)
    42  }
    43  
    44  /********
    45   * Main *
    46   ********/
    47  
    48  // Start Agent plugins selected for this example.
    49  func main() {
    50  	// Init close channel to stop the example.
    51  	exampleFinished := make(chan struct{})
    52  
    53  	// Inject dependencies to example plugin
    54  	ep := &ExamplePlugin{
    55  		Log:          logging.DefaultLogger,
    56  		VPP:          app.DefaultVPP(),
    57  		Orchestrator: &orchestrator.DefaultPlugin,
    58  	}
    59  
    60  	// Start Agent
    61  	a := agent.NewAgent(
    62  		agent.AllPlugins(ep),
    63  		agent.QuitOnClose(exampleFinished),
    64  	)
    65  	if err := a.Run(); err != nil {
    66  		log.Fatal()
    67  	}
    68  
    69  	go closeExample("localhost example finished", exampleFinished)
    70  }
    71  
    72  // Stop the agent with desired info message.
    73  func closeExample(message string, exampleFinished chan struct{}) {
    74  	time.Sleep(25 * time.Second)
    75  	logrus.DefaultLogger().Info(message)
    76  	close(exampleFinished)
    77  }
    78  
    79  /******************
    80   * Example plugin *
    81   ******************/
    82  
    83  // ExamplePlugin demonstrates the use of the localclient to locally transport example configuration into the default VPP plugins.
    84  type ExamplePlugin struct {
    85  	Log logging.Logger
    86  	app.VPP
    87  	Orchestrator *orchestrator.Plugin
    88  
    89  	wg     sync.WaitGroup
    90  	cancel context.CancelFunc
    91  }
    92  
    93  // PluginName represents name of plugin.
    94  const PluginName = "plugin-example"
    95  
    96  // Init initializes example plugin.
    97  func (p *ExamplePlugin) Init() error {
    98  	// Logger
    99  	p.Log = logrus.DefaultLogger()
   100  	p.Log.SetLevel(logging.DebugLevel)
   101  	p.Log.Info("Initializing VPP example")
   102  
   103  	logrus.DefaultLogger().Info("Initialization of the example plugin has completed")
   104  	return nil
   105  }
   106  
   107  // AfterInit initializes example plugin.
   108  func (p *ExamplePlugin) AfterInit() error {
   109  	// Apply initial VPP configuration.
   110  	p.resyncVPP()
   111  
   112  	// Schedule reconfiguration.
   113  	var ctx context.Context
   114  	ctx, p.cancel = context.WithCancel(context.Background())
   115  	p.wg.Add(1)
   116  	go p.reconfigureVPP(ctx)
   117  
   118  	return nil
   119  }
   120  
   121  // Close cleans up the resources.
   122  func (p *ExamplePlugin) Close() error {
   123  	p.cancel()
   124  	p.wg.Wait()
   125  
   126  	logrus.DefaultLogger().Info("Closed example plugin")
   127  	return nil
   128  }
   129  
   130  // String returns plugin name
   131  func (p *ExamplePlugin) String() string {
   132  	return PluginName
   133  }
   134  
   135  // resyncVPP propagates snapshot of the whole initial configuration to VPP plugins.
   136  func (p *ExamplePlugin) resyncVPP() {
   137  	err := localclient.DataResyncRequest(PluginName).
   138  		Interface(&memif1AsMaster).
   139  		Interface(&tap1Disabled).
   140  		Interface(&loopback1).
   141  		StaticRoute(&routeThroughMemif1).
   142  		Send().ReceiveReply()
   143  	if err != nil {
   144  		logrus.DefaultLogger().Errorf("Failed to apply initial VPP configuration: %v", err)
   145  	} else {
   146  		logrus.DefaultLogger().Info("Successfully applied initial VPP configuration")
   147  	}
   148  }
   149  
   150  // reconfigureVPP simulates a set of changes in the configuration related to VPP plugins.
   151  func (p *ExamplePlugin) reconfigureVPP(ctx context.Context) {
   152  	select {
   153  	case <-time.After(15 * time.Second):
   154  		// Simulate configuration change exactly 15seconds after resync.
   155  		err := localclient.DataChangeRequest(PluginName).
   156  			Put().
   157  			Interface(&memif1AsSlave).     /* turn memif1 into slave, remove the IP address */
   158  			Interface(&memif2).            /* newly added memif interface */
   159  			Interface(&tap1Enabled).       /* enable tap1 interface */
   160  			Interface(&loopback1WithAddr). /* assign IP address to loopback1 interface */
   161  			ACL(&acl1).                    /* declare ACL for the traffic leaving tap1 interface */
   162  			XConnect(&XConMemif1ToMemif2). /* xconnect memif interfaces */
   163  			BD(&BDLoopback1ToTap1).        /* put loopback and tap1 into the same bridge domain */
   164  			Delete().
   165  			StaticRoute("", 0, "192.168.2.1/32", "192.168.1.1"). /* remove the route going through memif1 */
   166  			Send().ReceiveReply()
   167  		if err != nil {
   168  			logrus.DefaultLogger().Errorf("Failed to reconfigure VPP: %v", err)
   169  		} else {
   170  			logrus.DefaultLogger().Info("Successfully reconfigured VPP")
   171  		}
   172  	case <-ctx.Done():
   173  		// cancel the scheduled re-configuration
   174  		logrus.DefaultLogger().Info("Planned VPP re-configuration was canceled")
   175  	}
   176  	p.wg.Done()
   177  }
   178  
   179  /*************************
   180   * Example plugin config *
   181   *************************/
   182  
   183  /*****************************************************
   184   * After Resync                                      *
   185   *                                                   *
   186   *  +---------------------------------------------+  *
   187   *  |                                             |  *
   188   *  +-----------+           +---------------------+  *
   189   *  | tap1      |           |  memif1             |  *
   190   *  | DISABLED  |      +--> |  MASTER             |  *
   191   *  +-----------+      |    |  IP: 192.168.1.1/24 |  *
   192   *  |                  |    +---------------------+  *
   193   *  |  +-----------+   |                          |  *
   194   *  |  | loopback1 |   +                          |  *
   195   *  |  +-----------+   route for 192.168.2.1      |  *
   196   *  |                                             |  *
   197   *  +---------------------------------------------+  *
   198   *                                                   *
   199   *****************************************************/
   200  
   201  /********************************************************
   202   * After Data Change Request                            *
   203   *                                                      *
   204   *  +------------------------------------------------+  *
   205   *  |                                                |  *
   206   *  +---------+ +------+                  +----------+  *
   207   *  | tap1    |-| acl1 |-+         +------| memif1   |  *
   208   *  | ENABLED | +------+ |         |      | SLAVE    |  *
   209   *  +---------+          |         |      +----------+  *
   210   *  |                  Bridge   xconnect             |  *
   211   *  |                  domain      |      +----------+  *
   212   *  |                    |         |      | memif2   |  *
   213   *  |  +------------+    |         +------| SLAVE    |  *
   214   *  |  | loopback1  |----+                +----------|  *
   215   *  |  +------------+                                |  *
   216   *  |                                                |  *
   217   *  +------------------------------------------------+  *
   218   *                                                      *
   219   ********************************************************/
   220  
   221  var (
   222  	// memif1AsMaster is an example of a memory interface configuration. (Master=true, with IPv4 address).
   223  	memif1AsMaster = interfaces.Interface{
   224  		Name:        "memif1",
   225  		Type:        interfaces.Interface_MEMIF,
   226  		Enabled:     true,
   227  		Mtu:         1500,
   228  		IpAddresses: []string{"192.168.1.1/24"},
   229  		Link: &interfaces.Interface_Memif{
   230  			Memif: &interfaces.MemifLink{
   231  				Id:             1,
   232  				Master:         true,
   233  				SocketFilename: "/tmp/memif1.sock",
   234  			},
   235  		},
   236  	}
   237  
   238  	// memif1AsSlave is the original memif1 turned into slave and stripped of the IP address.
   239  	memif1AsSlave = interfaces.Interface{
   240  		Name:    "memif1",
   241  		Type:    interfaces.Interface_MEMIF,
   242  		Enabled: true,
   243  		Mtu:     1500,
   244  		Link: &interfaces.Interface_Memif{
   245  			Memif: &interfaces.MemifLink{
   246  				Id:             1,
   247  				Master:         false,
   248  				SocketFilename: "/tmp/memif1.sock",
   249  			},
   250  		},
   251  	}
   252  
   253  	// Memif2 is a slave memif without IP address and to be xconnected with memif1.
   254  	memif2 = interfaces.Interface{
   255  		Name:    "memif2",
   256  		Type:    interfaces.Interface_MEMIF,
   257  		Enabled: true,
   258  		Mtu:     1500,
   259  		Link: &interfaces.Interface_Memif{
   260  			Memif: &interfaces.MemifLink{
   261  				Id:             2,
   262  				Master:         false,
   263  				SocketFilename: "/tmp/memif2.sock",
   264  			},
   265  		},
   266  	}
   267  
   268  	// XConMemif1ToMemif2 defines xconnect between memifs.
   269  	XConMemif1ToMemif2 = l2.XConnectPair{
   270  		ReceiveInterface:  memif1AsSlave.Name,
   271  		TransmitInterface: memif2.Name,
   272  	}
   273  
   274  	// tap1Disabled is a disabled tap interface.
   275  	tap1Disabled = interfaces.Interface{
   276  		Name:    "tap1",
   277  		Type:    interfaces.Interface_TAP,
   278  		Enabled: false,
   279  		Link: &interfaces.Interface_Tap{
   280  			Tap: &interfaces.TapLink{
   281  				Version:    2,
   282  				HostIfName: "linux-tap1",
   283  			},
   284  		},
   285  		Mtu: 1500,
   286  	}
   287  
   288  	// tap1Enabled is an enabled tap1 interface.
   289  	tap1Enabled = interfaces.Interface{
   290  		Name:    "tap1",
   291  		Type:    interfaces.Interface_TAP,
   292  		Enabled: true,
   293  		Link: &interfaces.Interface_Tap{
   294  			Tap: &interfaces.TapLink{
   295  				Version:    2,
   296  				HostIfName: "linux-tap1",
   297  			},
   298  		},
   299  		Mtu: 1500,
   300  	}
   301  
   302  	acl1 = acl.ACL{
   303  		Name: "acl1",
   304  		Rules: []*acl.ACL_Rule{
   305  			{
   306  				Action: acl.ACL_Rule_DENY,
   307  				IpRule: &acl.ACL_Rule_IpRule{
   308  					Ip: &acl.ACL_Rule_IpRule_Ip{
   309  						DestinationNetwork: "10.1.1.0/24",
   310  						SourceNetwork:      "10.1.2.0/24",
   311  					},
   312  					Tcp: &acl.ACL_Rule_IpRule_Tcp{
   313  						DestinationPortRange: &acl.ACL_Rule_IpRule_PortRange{
   314  							LowerPort: 50,
   315  							UpperPort: 150,
   316  						},
   317  						SourcePortRange: &acl.ACL_Rule_IpRule_PortRange{
   318  							LowerPort: 1000,
   319  							UpperPort: 2000,
   320  						},
   321  					},
   322  				},
   323  			},
   324  		},
   325  		Interfaces: &acl.ACL_Interfaces{
   326  			Egress: []string{"tap1"},
   327  		},
   328  	}
   329  
   330  	// loopback1 is an example of a loopback interface configuration (without IP address assigned).
   331  	loopback1 = interfaces.Interface{
   332  		Name:    "loopback1",
   333  		Type:    interfaces.Interface_SOFTWARE_LOOPBACK,
   334  		Enabled: true,
   335  		Mtu:     1500,
   336  	}
   337  
   338  	// loopback1WithAddr extends loopback1 definition with an IP address.
   339  	loopback1WithAddr = interfaces.Interface{
   340  		Name:        "loopback1",
   341  		Type:        interfaces.Interface_SOFTWARE_LOOPBACK,
   342  		Enabled:     true,
   343  		Mtu:         1500,
   344  		IpAddresses: []string{"10.0.0.1/24"},
   345  	}
   346  
   347  	// BDLoopback1ToTap1 is a bridge domain with tap1 and loopback1 interfaces in it.
   348  	// Loopback is set to be BVI.
   349  	BDLoopback1ToTap1 = l2.BridgeDomain{
   350  		Name:                "br1",
   351  		Flood:               false,
   352  		UnknownUnicastFlood: false,
   353  		Forward:             true,
   354  		Learn:               true,
   355  		ArpTermination:      false,
   356  		MacAge:              0, /* means disable aging */
   357  		Interfaces: []*l2.BridgeDomain_Interface{
   358  			{
   359  				Name:                    "loopback1",
   360  				BridgedVirtualInterface: true,
   361  			}, {
   362  				Name:                    "tap1",
   363  				BridgedVirtualInterface: false,
   364  			},
   365  		},
   366  	}
   367  
   368  	// routeThroughMemif1 is an example route configuration with memif1 being the next hop.
   369  	routeThroughMemif1 = l3.Route{
   370  		VrfId:       0,
   371  		DstNetwork:  "192.168.2.1/32",
   372  		NextHopAddr: "192.168.1.1", // Memif1AsMaster
   373  		Weight:      5,
   374  	}
   375  )