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 )