diff --git a/net.go b/net.go index 99a57aa..2f6d3f2 100644 --- a/net.go +++ b/net.go @@ -191,6 +191,24 @@ type Tunnel struct { UsePmtu string `json:"usePmtu,omitempty"` } +// Vxlans contains a list of vlxan profiles on the BIG-IP system. +type Vxlans struct { + Vxlans []Vxlan `json:"items"` +} + +// Vxlan is the structure for the VXLAN profile on the bigip. +// https://devcentral.f5.com/wiki/iControlREST.APIRef_tm_net_tunnels_vxlan.ashx +type Vxlan struct { + Name string `json:"name,omitempty"` + AppService string `json:"appService,omitempty"` + DefaultsFrom string `json:"defaultsFrom,omitempty"` + Description string `json:"description,omitempty"` + EncapsulationType string `json:"encapsulationType,omitempty"` + FloodingType string `json:"floodingType,omitempty"` + Partition string `json:"partition,omitempty"` + Port int `json:"port,omitempty"` +} + const ( uriNet = "net" uriInterface = "interface" @@ -205,6 +223,21 @@ const ( uriRouteDomain = "route-domain" ) +// formatResourceID takes the resource name to +// ensure theres a partition for the Resource ID +func formatResourceID(name string) string { + // If the name specifies the partition already, then + // just hand it back. + regex := regexp.MustCompile(`^~([a-zA-Z0-9-.]+)~`) + if regex.MatchString(name) { + return name + } + + // Otherwise, tack on the Common partition + // for best practices with the resource_id. + return "~Common~" + name +} + // Interfaces returns a list of interfaces. func (b *BigIP) Interfaces() (*Interfaces, error) { var interfaces Interfaces @@ -494,15 +527,7 @@ func (b *BigIP) Tunnels() (*Tunnels, error) { // GetTunnel fetches the tunnel by it's name. func (b *BigIP) GetTunnel(name string) (*Tunnel, error) { var tunnel Tunnel - values := []string{} - regex := regexp.MustCompile(`^(\/.+\/)?(.+)`) - match := regex.FindStringSubmatch(name) - if match[1] == "" { - values = append(values, "~Common~") - } - values = append(values, name) - // Join the strings into one. - result := strings.Join(values, "") + result := formatResourceID(name) err, ok := b.getForEntity(&tunnel, uriNet, uriTunnels, uriTunnel, result) if err != nil { return nil, err @@ -514,6 +539,11 @@ func (b *BigIP) GetTunnel(name string) (*Tunnel, error) { return &tunnel, nil } +// AddTunnel adds a new tunnel to the BIG-IP system from a config. +func (b *BigIP) AddTunnel(config *Tunnel) error { + return b.post(config, uriNet, uriTunnels, uriTunnel) +} + // CreateTunnel adds a new tunnel to the BIG-IP system. func (b *BigIP) CreateTunnel(name, profile string) error { config := &Tunnel{ @@ -533,3 +563,53 @@ func (b *BigIP) DeleteTunnel(name string) error { func (b *BigIP) ModifyTunnel(name string, config *Tunnel) error { return b.put(config, uriNet, uriTunnels, uriTunnel, name) } + +// Vxlans returns a list of vxlan profiles. +func (b *BigIP) Vxlans() ([]Vxlan, error) { + var vxlans Vxlans + err, _ := b.getForEntity(&vxlans, uriNet, uriTunnels, uriVxlan) + if err != nil { + return nil, err + } + + return vxlans.Vxlans, nil +} + +// GetVxlan fetches the vxlan profile by it's name. +func (b *BigIP) GetVxlan(name string) (*Vxlan, error) { + var vxlan Vxlan + result := formatResourceID(name) + err, ok := b.getForEntity(&vxlan, uriNet, uriTunnels, uriVxlan, result) + if err != nil { + return nil, err + } + if !ok { + return nil, nil + } + + return &vxlan, nil +} + +// AddVxlan adds a new vxlan profile to the BIG-IP system. +func (b *BigIP) AddVxlan(config *Vxlan) error { + return b.post(config, uriNet, uriTunnels, uriVxlan) +} + +// CreateVxlan adds a new vxlan profile to the BIG-IP system. +func (b *BigIP) CreateVxlan(name string) error { + config := &Vxlan{ + Name: name, + } + + return b.post(config, uriNet, uriTunnels, uriVxlan) +} + +// DeleteVxlan removes a vxlan profile. +func (b *BigIP) DeleteVxlan(name string) error { + return b.delete(uriNet, uriTunnels, uriVxlan, name) +} + +// ModifyVxlan allows you to change any attribute of a vxlan profile. +func (b *BigIP) ModifyVxlan(name string, config *Vxlan) error { + return b.put(config, uriNet, uriTunnels, uriVxlan, name) +} diff --git a/net_test.go b/net_test.go index aa6f95c..bf9ad8a 100644 --- a/net_test.go +++ b/net_test.go @@ -494,6 +494,33 @@ func (s *NetTestSuite) TestCreateTunnel() { assertRestCall(s, "POST", "/mgmt/tm/net/tunnels/tunnel", `{"name":"some-foo-tunnel", "profile":"/Common/some-foo-profile"}`) } +func (s *NetTestSuite) TestAddTunnel() { + someTunnel := Tunnel{ + Name: "foo-tunnel", + AppService: "foo-appservice", + AutoLasthop: "foo-lasthop", + Description: "foo-desc", + IdleTimeout: 123, + IfIndex: 456, + Key: 789, + LocalAddress: "foo-local-address", + Mode: "foo-mode", + Mtu: 1440, + Partition: "foo-partition", + Profile: "foo-profile", + RemoteAddress: "foo-remoteaddr", + SecondaryAddress: "foo-secondaddr", + Tos: "foo-tos", + TrafficGroup: "foo-tg", + Transparent: "foo-transparent", + UsePmtu: "foo-pmtu", + } + err := s.Client.AddTunnel(&someTunnel) + + assert.Nil(s.T(), err) + assertRestCall(s, "POST", "/mgmt/tm/net/tunnels/tunnel", `{"appService":"foo-appservice", "autoLasthop":"foo-lasthop", "description":"foo-desc", "idleTimeout":123, "ifIndex":456, "key":789, "localAddress":"foo-local-address", "mode":"foo-mode", "mtu":1440, "name":"foo-tunnel", "partition":"foo-partition", "profile":"foo-profile", "remoteAddress":"foo-remoteaddr", "secondaryAddress":"foo-secondaddr", "tos":"foo-tos", "trafficGroup":"foo-tg", "transparent":"foo-transparent", "usePmtu":"foo-pmtu"}`) +} + func (s *NetTestSuite) TestDeleteTunnel() { err := s.Client.DeleteTunnel("some-foo-tunnel") @@ -509,3 +536,123 @@ func (s *NetTestSuite) TestModifyTunnel() { assert.Nil(s.T(), err) assertRestCall(s, "PUT", "/mgmt/tm/net/tunnels/tunnel/some-foo-tunnel", `{"transparent":"enabled"}`) } + +var goodVxlansRespnse = `{ + "items": [ + { + "defaultsFrom": "/Common/vxlan", + "defaultsFromReference": { + "link": "https://localhost/mgmt/tm/net/tunnels/vxlan/~Common~vxlan?ver=13.1.1.2" + }, + "encapsulationType": "vxlan", + "floodingType": "multipoint", + "fullPath": "/Common/vxlan-foo", + "generation": 1, + "kind": "tm:net:tunnels:vxlan:vxlanstate", + "name": "vxlan-foo", + "partition": "foo", + "port": 4789, + "selfLink": "https://localhost/mgmt/tm/net/tunnels/vxlan/~foo~vxlan-foo?ver=13.1.1.2" + }, + { + "defaultsFrom": "/Common/vxlan", + "defaultsFromReference": { + "link": "https://localhost/mgmt/tm/net/tunnels/vxlan/~Common~vxlan?ver=13.1.1.2" + }, + "encapsulationType": "vxlan", + "floodingType": "none", + "fullPath": "/Common/vxlan-bar", + "generation": 1, + "kind": "tm:net:tunnels:vxlan:vxlanstate", + "name": "vxlan-bar", + "partition": "bar", + "port": 4789, + "selfLink": "https://localhost/mgmt/tm/net/tunnels/vxlan/~bar~vxlan-bar?ver=13.1.1.2" + } + ], + "kind": "tm:net:tunnels:vxlan:vxlancollectionstate", + "selfLink": "https://localhost/mgmt/tm/net/tunnels/vxlan?ver=13.1.1.2" +}` + +var goodVxlanRespnse = `{ + "defaultsFrom": "/Common/vxlan", + "defaultsFromReference": { + "link": "https://localhost/mgmt/tm/net/tunnels/vxlan/~Common~vxlan?ver=13.1.1.2" + }, + "encapsulationType": "vxlan", + "floodingType": "multipoint", + "fullPath": "/Common/vxlan-foo", + "generation": 1, + "kind": "tm:net:tunnels:vxlan:vxlanstate", + "name": "vxlan-foo", + "partition": "foo", + "port": 4789, + "selfLink": "https://localhost/mgmt/tm/net/tunnels/vxlan/~foo~vxlan-foo?ver=13.1.1.2" +}` + +func (s *NetTestSuite) TestVxlans() { + s.ResponseFunc = func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(goodVxlansRespnse)) + } + + vxlans, err := s.Client.Vxlans() + + assert.Nil(s.T(), err) + assertRestCall(s, "GET", "/mgmt/tm/net/tunnels/vxlan", "") + assert.Equal(s.T(), 2, len(vxlans)) + assert.Equal(s.T(), "vxlan-foo", vxlans[0].Name) + assert.Equal(s.T(), "vxlan-bar", vxlans[1].Name) +} + +func (s *NetTestSuite) TestGetVxlan() { + s.ResponseFunc = func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(goodVxlanRespnse)) + } + + vxlan, err := s.Client.GetVxlan("~foo~vxlan-foo") + + assert.Nil(s.T(), err) + assertRestCall(s, "GET", "/mgmt/tm/net/tunnels/vxlan/~foo~vxlan-foo", "") + assert.Equal(s.T(), "vxlan-foo", vxlan.Name) + assert.Equal(s.T(), 4789, vxlan.Port) +} + +func (s *NetTestSuite) TestCreateVxlan() { + err := s.Client.CreateVxlan("some-foo-vxlan") + + assert.Nil(s.T(), err) + assertRestCall(s, "POST", "/mgmt/tm/net/tunnels/vxlan", `{"name":"some-foo-vxlan"}`) +} + +func (s *NetTestSuite) TestAddVxlan() { + someVxlan := Vxlan{ + Name: "foo-vxlan", + AppService: "foo-appservice", + Description: "foo-desc", + DefaultsFrom: "foo-base-profile", + EncapsulationType: "foo-encap", + FloodingType: "foo-ft", + Partition: "foo-partition", + Port: 123, + } + err := s.Client.AddVxlan(&someVxlan) + + assert.Nil(s.T(), err) + assertRestCall(s, "POST", "/mgmt/tm/net/tunnels/vxlan", `{"appService":"foo-appservice", "defaultsFrom":"foo-base-profile", "description":"foo-desc", "encapsulationType":"foo-encap", "floodingType":"foo-ft", "name":"foo-vxlan", "partition":"foo-partition", "port":123}`) +} + +func (s *NetTestSuite) TestDeleteVxlan() { + err := s.Client.DeleteVxlan("some-foo-vxlan") + + assert.Nil(s.T(), err) + assertRestCall(s, "DELETE", "/mgmt/tm/net/tunnels/vxlan/some-foo-vxlan", "") +} + +func (s *NetTestSuite) TestModifyVxlan() { + vxlan := &Vxlan{Port: 456} + + err := s.Client.ModifyVxlan("some-foo-vxlan", vxlan) + + assert.Nil(s.T(), err) + assertRestCall(s, "PUT", "/mgmt/tm/net/tunnels/vxlan/some-foo-vxlan", `{"port":456}`) +}