hs-test: refactor netconfig

This joins separate representations of veth and tap interfaces
into a single struct. It removes the need for type interface
and embedding which simplifies the code.

Type: test
Signed-off-by: Maros Ondrejicka <mondreji@cisco.com>
Change-Id: I1b2c368bfe90a5bdfaaa9a5129c27d7d96f8fe3b
diff --git a/extras/hs-test/hst_suite.go b/extras/hs-test/hst_suite.go
index 01be2ef..ff70245 100644
--- a/extras/hs-test/hst_suite.go
+++ b/extras/hs-test/hst_suite.go
@@ -25,7 +25,7 @@
 	containers    map[string]*Container
 	volumes       []string
 	netConfigs    []NetConfig
-	netInterfaces map[string]NetInterface
+	netInterfaces map[string]*NetInterface
 	addresser     *Addresser
 	testIds       map[string]string
 }
@@ -188,7 +188,7 @@
 	}
 
 	s.addresser = NewAddresser(s)
-	s.netInterfaces = make(map[string]NetInterface)
+	s.netInterfaces = make(map[string]*NetInterface)
 	for _, elem := range yamlTopo.Devices {
 		switch elem["type"].(string) {
 		case NetNs:
@@ -199,20 +199,11 @@
 					s.T().Fatalf("network config error: %v", err)
 				}
 			}
-		case Veth:
+		case Veth, Tap:
 			{
-				if veth, err := NewVeth(elem, s.addresser); err == nil {
-					s.netConfigs = append(s.netConfigs, &veth)
-					s.netInterfaces[veth.Name()] = &veth
-				} else {
-					s.T().Fatalf("network config error: %v", err)
-				}
-			}
-		case Tap:
-			{
-				if tap, err := NewTap(elem, s.addresser); err == nil {
-					s.netConfigs = append(s.netConfigs, &tap)
-					s.netInterfaces[tap.Name()] = &tap
+				if netIf, err := NewNetworkInterface(elem, s.addresser); err == nil {
+					s.netConfigs = append(s.netConfigs, netIf)
+					s.netInterfaces[netIf.Name()] = netIf
 				} else {
 					s.T().Fatalf("network config error: %v", err)
 				}
@@ -262,11 +253,6 @@
 	return s.testIds[testName]
 }
 
-type NetworkAddresses struct {
-	network           int
-	numberOfAddresses int
-}
-
 type AddressCounter = int
 
 type Addresser struct {
diff --git a/extras/hs-test/http_test.go b/extras/hs-test/http_test.go
index 93705b2..96985be 100644
--- a/extras/hs-test/http_test.go
+++ b/extras/hs-test/http_test.go
@@ -51,7 +51,7 @@
 	err := vpp.waitForApp("-app", 5)
 	s.assertNil(err)
 
-	serverAddress := s.netInterfaces[tapNameVpp].IP4AddressString()
+	serverAddress := s.netInterfaces[tapInterfaceName].Peer().IP4AddressString()
 
 	defer func() { os.Remove(query) }()
 	go startWget(finished, serverAddress, "80", query, "")
@@ -64,7 +64,7 @@
 	var args []string
 	var exeName string
 
-	serverAddress := s.netInterfaces[tapNameVpp].IP4AddressString()
+	serverAddress := s.netInterfaces[tapInterfaceName].Peer().IP4AddressString()
 
 	if ab_or_wrk == "ab" {
 		args = []string{"-n", fmt.Sprintf("%d", nRequests), "-c",
diff --git a/extras/hs-test/linux_iperf_test.go b/extras/hs-test/linux_iperf_test.go
index 2c3437f..954b54e 100644
--- a/extras/hs-test/linux_iperf_test.go
+++ b/extras/hs-test/linux_iperf_test.go
@@ -14,7 +14,7 @@
 	s.assertNil(err)
 	s.log("server running")
 
-	ipAddress := s.netInterfaces["tap0"].IP4AddressString()
+	ipAddress := s.netInterfaces[tapInterfaceName].IP4AddressString()
 	go StartClientApp(ipAddress, nil, clnCh, clnRes)
 	s.log("client running")
 	s.log(<-clnRes)
diff --git a/extras/hs-test/netconfig.go b/extras/hs-test/netconfig.go
index db6cbe1..f4dce7d 100644
--- a/extras/hs-test/netconfig.go
+++ b/extras/hs-test/netconfig.go
@@ -12,6 +12,7 @@
 )
 
 type (
+	Cmd                  = exec.Cmd
 	MacAddress           = ethernet_types.MacAddress
 	AddressWithPrefix    = ip_types.AddressWithPrefix
 	IP4AddressWithPrefix = ip_types.IP4AddressWithPrefix
@@ -29,18 +30,7 @@
 		category string // what else to call this when `type` is reserved?
 	}
 
-	NetInterface interface {
-		NetConfig
-		SetAddress(string)
-		AddressWithPrefix() AddressWithPrefix
-		IP4AddressWithPrefix() IP4AddressWithPrefix
-		IP4AddressString() string
-		SetIndex(InterfaceIndex)
-		Index() InterfaceIndex
-		HwAddress() MacAddress
-	}
-
-	NetInterfaceBase struct {
+	NetInterface struct {
 		NetConfigBase
 		addresser        *Addresser
 		ip4Address       string // this will have form 10.10.10.1/24
@@ -48,18 +38,7 @@
 		hwAddress        MacAddress
 		networkNamespace string
 		networkNumber    int
-	}
-
-	NetworkInterfaceVeth struct {
-		NetInterfaceBase
-		peerNetworkNamespace string
-		peerName             string
-		peerNetworkNumber    int
-		peerIp4Address       string
-	}
-
-	NetworkInterfaceTap struct {
-		NetInterfaceBase
+		peer             *NetInterface
 	}
 
 	NetworkNamespace struct {
@@ -80,6 +59,181 @@
 	Bridge string = "bridge"
 )
 
+type InterfaceAdder func(n *NetInterface) *Cmd
+
+var (
+	ipCommandMap = map[string]InterfaceAdder{
+		Veth: func(n *NetInterface) *Cmd {
+			return exec.Command("ip", "link", "add", n.name, "type", "veth", "peer", "name", n.peer.name)
+		},
+		Tap: func(n *NetInterface) *Cmd {
+			return exec.Command("ip", "tuntap", "add", n.name, "mode", "tap")
+		},
+	}
+)
+
+func NewNetworkInterface(cfg NetDevConfig, a *Addresser) (*NetInterface, error) {
+	var newInterface *NetInterface = &NetInterface{}
+	var err error
+	newInterface.addresser = a
+	newInterface.name = cfg["name"].(string)
+	newInterface.networkNumber = defaultNetworkNumber
+
+	if interfaceType, ok := cfg["type"]; ok {
+		newInterface.category = interfaceType.(string)
+	}
+
+	if presetHwAddress, ok := cfg["preset-hw-address"]; ok {
+		newInterface.hwAddress, err = ethernet_types.ParseMacAddress(presetHwAddress.(string))
+		if err != nil {
+			return &NetInterface{}, err
+		}
+	}
+
+	if netns, ok := cfg["netns"]; ok {
+		newInterface.networkNamespace = netns.(string)
+	}
+
+	if ip, ok := cfg["ip4"]; ok {
+		if n, ok := ip.(NetDevConfig)["network"]; ok {
+			newInterface.networkNumber = n.(int)
+		}
+		newInterface.ip4Address, err = newInterface.addresser.NewIp4Address(
+			newInterface.networkNumber,
+		)
+		if err != nil {
+			return &NetInterface{}, err
+		}
+	}
+
+	if _, ok := cfg["peer"]; !ok {
+		return newInterface, nil
+	}
+
+	peer := cfg["peer"].(NetDevConfig)
+
+	if newInterface.peer, err = NewNetworkInterface(peer, a); err != nil {
+		return &NetInterface{}, err
+	}
+
+	return newInterface, nil
+}
+
+func (n *NetInterface) ConfigureUpState() error {
+	err := SetDevUp(n.Name(), "")
+	if err != nil {
+		return fmt.Errorf("set link up failed: %v", err)
+	}
+	return nil
+}
+
+func (n *NetInterface) ConfigureNetworkNamespace() error {
+	if n.networkNamespace != "" {
+		err := LinkSetNetns(n.name, n.networkNamespace)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (n *NetInterface) ConfigureAddress() error {
+	if n.ip4Address != "" {
+		if err := AddAddress(
+			n.Name(),
+			n.ip4Address,
+			n.networkNamespace,
+		); err != nil {
+			return err
+		}
+
+	}
+	return nil
+}
+
+func (n *NetInterface) Configure() error {
+	cmd := ipCommandMap[n.Type()](n)
+	_, err := cmd.CombinedOutput()
+	if err != nil {
+		return fmt.Errorf("creating interface '%v' failed: %v", n.Name(), err)
+	}
+
+	if err := n.ConfigureUpState(); err != nil {
+		return err
+	}
+
+	if err := n.ConfigureNetworkNamespace(); err != nil {
+		return err
+	}
+
+	if err := n.ConfigureAddress(); err != nil {
+		return err
+	}
+
+	if n.peer != nil && n.peer.name != "" {
+		if err := n.Peer().ConfigureUpState(); err != nil {
+			return err
+		}
+
+		if err := n.Peer().ConfigureNetworkNamespace(); err != nil {
+			return err
+		}
+
+		if err := n.Peer().ConfigureAddress(); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (n *NetInterface) Unconfigure() {
+	DelLink(n.name)
+}
+
+func (n *NetInterface) Name() string {
+	return n.name
+}
+
+func (n *NetInterface) Type() string {
+	return n.category
+}
+
+func (n *NetInterface) SetAddress(address string) {
+	n.ip4Address = address
+}
+
+func (n *NetInterface) SetIndex(index InterfaceIndex) {
+	n.index = index
+}
+
+func (n *NetInterface) Index() InterfaceIndex {
+	return n.index
+}
+
+func (n *NetInterface) AddressWithPrefix() AddressWithPrefix {
+	address, _ := ip_types.ParseAddressWithPrefix(n.ip4Address)
+	return address
+}
+
+func (n *NetInterface) IP4AddressWithPrefix() IP4AddressWithPrefix {
+	ip4Prefix, _ := ip_types.ParseIP4Prefix(n.ip4Address)
+	ip4AddressWithPrefix := ip_types.IP4AddressWithPrefix(ip4Prefix)
+	return ip4AddressWithPrefix
+}
+
+func (n *NetInterface) IP4AddressString() string {
+	return strings.Split(n.ip4Address, "/")[0]
+}
+
+func (n *NetInterface) HwAddress() MacAddress {
+	return n.hwAddress
+}
+
+func (n *NetInterface) Peer() *NetInterface {
+	return n.peer
+}
+
 func (b *NetConfigBase) Name() string {
 	return b.name
 }
@@ -88,165 +242,10 @@
 	return b.category
 }
 
-func (b *NetInterfaceBase) SetAddress(address string) {
-	b.ip4Address = address
-}
-
-func (b *NetInterfaceBase) SetIndex(index InterfaceIndex) {
-	b.index = index
-}
-
-func (b *NetInterfaceBase) Index() InterfaceIndex {
-	return b.index
-}
-
-func (b *NetInterfaceBase) AddressWithPrefix() AddressWithPrefix {
-	address, _ := ip_types.ParseAddressWithPrefix(b.ip4Address)
-	return address
-}
-
-func (b *NetInterfaceBase) IP4AddressWithPrefix() IP4AddressWithPrefix {
-	IP4Prefix, _ := ip_types.ParseIP4Prefix(b.ip4Address)
-	IP4AddressWithPrefix := ip_types.IP4AddressWithPrefix(IP4Prefix)
-	return IP4AddressWithPrefix
-}
-
-func (b *NetInterfaceBase) IP4AddressString() string {
-	return strings.Split(b.ip4Address, "/")[0]
-}
-
-func (b *NetInterfaceBase) HwAddress() MacAddress {
-	return b.hwAddress
-}
-
-func NewVeth(cfg NetDevConfig, a *Addresser) (NetworkInterfaceVeth, error) {
-	var veth NetworkInterfaceVeth
-	var err error
-	veth.addresser = a
-	veth.name = cfg["name"].(string)
-	veth.category = "veth"
-	veth.peerNetworkNumber = defaultNetworkNumber
-
-	if cfg["preset-hw-address"] != nil {
-		veth.hwAddress, err = ethernet_types.ParseMacAddress(cfg["preset-hw-address"].(string))
-		if err != nil {
-			return NetworkInterfaceVeth{}, err
-		}
-	}
-
-	if netns, ok := cfg["netns"]; ok {
-		veth.networkNamespace = netns.(string)
-	}
-
-	if ip, ok := cfg["ip4"]; ok {
-		if n, ok := ip.(NetDevConfig)["network"]; ok {
-			veth.networkNumber = n.(int)
-		}
-		veth.ip4Address, err = veth.addresser.NewIp4Address(veth.networkNumber)
-		if err != nil {
-			return NetworkInterfaceVeth{}, err
-		}
-	}
-
-	peer := cfg["peer"].(NetDevConfig)
-
-	veth.peerName = peer["name"].(string)
-
-	if peer["netns"] != nil {
-		veth.peerNetworkNamespace = peer["netns"].(string)
-	}
-
-	if peerIp, ok := peer["ip4"]; ok {
-		if n, ok := peerIp.(NetDevConfig)["network"]; ok {
-			veth.peerNetworkNumber = n.(int)
-		}
-		veth.peerIp4Address, err = veth.addresser.NewIp4Address(veth.peerNetworkNumber)
-		if err != nil {
-			return NetworkInterfaceVeth{}, err
-		}
-	}
-
-	return veth, nil
-}
-
-func (iface *NetworkInterfaceVeth) Configure() error {
-	err := AddVethPair(iface.name, iface.peerName)
-	if err != nil {
-		return err
-	}
-
-	if iface.networkNamespace != "" {
-		err := LinkSetNetns(iface.name, iface.networkNamespace)
-		if err != nil {
-			return err
-		}
-	}
-
-	if iface.peerNetworkNamespace != "" {
-		err := LinkSetNetns(iface.peerName, iface.peerNetworkNamespace)
-		if err != nil {
-			return err
-		}
-	}
-
-	if iface.ip4Address != "" {
-		err = AddAddress(
-			iface.Name(),
-			iface.ip4Address,
-			iface.networkNamespace,
-		)
-	}
-
-	if iface.peerIp4Address != "" {
-		err = AddAddress(
-			iface.peerName,
-			iface.peerIp4Address,
-			iface.peerNetworkNamespace,
-		)
-		if err != nil {
-			return fmt.Errorf("failed to add configure address for %s: %v", iface.peerName, err)
-		}
-	}
-	return nil
-}
-
-func (iface *NetworkInterfaceVeth) Unconfigure() {
-	DelLink(iface.name)
-}
-
-func (iface *NetworkInterfaceVeth) PeerIp4AddressString() string {
-	return strings.Split(iface.peerIp4Address, "/")[0]
-}
-
-func NewTap(cfg NetDevConfig, a *Addresser) (NetworkInterfaceTap, error) {
-	var tap NetworkInterfaceTap
-	tap.addresser = a
-	tap.name = cfg["name"].(string)
-	tap.category = "tap"
-	ip4Address, err := tap.addresser.NewIp4Address()
-	if err != nil {
-		return NetworkInterfaceTap{}, err
-	}
-	tap.SetAddress(ip4Address)
-	return tap, nil
-}
-
-func (iface *NetworkInterfaceTap) Configure() error {
-	err := AddTap(iface.name, iface.IP4AddressString())
-	if err != nil {
-		return err
-	}
-	return nil
-}
-
-func (iface *NetworkInterfaceTap) Unconfigure() {
-	DelLink(iface.name)
-}
-
 func NewNetNamespace(cfg NetDevConfig) (NetworkNamespace, error) {
 	var networkNamespace NetworkNamespace
 	networkNamespace.name = cfg["name"].(string)
-	networkNamespace.category = "netns"
+	networkNamespace.category = NetNs
 	return networkNamespace, nil
 }
 
@@ -261,7 +260,7 @@
 func NewBridge(cfg NetDevConfig) (NetworkBridge, error) {
 	var bridge NetworkBridge
 	bridge.name = cfg["name"].(string)
-	bridge.category = "bridge"
+	bridge.category = Bridge
 	for _, v := range cfg["interfaces"].([]interface{}) {
 		bridge.interfaces = append(bridge.interfaces, v.(string))
 	}
@@ -303,30 +302,6 @@
 	return setDevUpDown(dev, ns, false)
 }
 
-func AddTap(ifName, ifAddress string) error {
-	cmd := exec.Command("ip", "tuntap", "add", ifName, "mode", "tap")
-	o, err := cmd.CombinedOutput()
-	if err != nil {
-		s := fmt.Sprintf("error creating tap %s: %v: %s", ifName, err, string(o))
-		return errors.New(s)
-	}
-
-	cmd = exec.Command("ip", "addr", "add", ifAddress, "dev", ifName)
-	err = cmd.Run()
-	if err != nil {
-		DelLink(ifName)
-		s := fmt.Sprintf("error setting addr for tap %s: %v", ifName, err)
-		return errors.New(s)
-	}
-
-	err = SetDevUp(ifName, "")
-	if err != nil {
-		DelLink(ifName)
-		return err
-	}
-	return nil
-}
-
 func DelLink(ifName string) {
 	cmd := exec.Command("ip", "link", "del", ifName)
 	cmd.Run()
@@ -349,23 +324,6 @@
 	return nil
 }
 
-func AddVethPair(ifName, peerName string) error {
-	cmd := exec.Command("ip", "link", "add", ifName, "type", "veth", "peer", "name", peerName)
-	err := cmd.Run()
-	if err != nil {
-		return fmt.Errorf("creating veth pair '%v/%v' failed: %v", ifName, peerName, err)
-	}
-	err = SetDevUp(ifName, "")
-	if err != nil {
-		return fmt.Errorf("set link up failed: %v", err)
-	}
-	err = SetDevUp(peerName, "")
-	if err != nil {
-		return fmt.Errorf("set link up failed: %v", err)
-	}
-	return nil
-}
-
 func addDelNetns(name string, isAdd bool) error {
 	var op string
 	if isAdd {
diff --git a/extras/hs-test/proxy_test.go b/extras/hs-test/proxy_test.go
index d8918f1..f121866 100644
--- a/extras/hs-test/proxy_test.go
+++ b/extras/hs-test/proxy_test.go
@@ -50,14 +50,14 @@
 }
 
 func configureVppProxy(s *NsSuite) error {
-	serverVeth := s.netInterfaces[serverInterface].(*NetworkInterfaceVeth)
+	serverVeth := s.netInterfaces[serverInterface]
 	clientVeth := s.netInterfaces[clientInterface]
 
 	testVppProxy := s.getContainerByName("vpp").vppInstance
 	output := testVppProxy.vppctl(
 		"test proxy server server-uri tcp://%s/555 client-uri tcp://%s/666",
 		clientVeth.IP4AddressString(),
-		serverVeth.PeerIp4AddressString(),
+		serverVeth.Peer().IP4AddressString(),
 	)
 	s.log("proxy configured...", output)
 	return nil
diff --git a/extras/hs-test/suite_no_topo_test.go b/extras/hs-test/suite_no_topo_test.go
index 01958b0..12b939e 100644
--- a/extras/hs-test/suite_no_topo_test.go
+++ b/extras/hs-test/suite_no_topo_test.go
@@ -4,8 +4,7 @@
 	singleTopoContainerVpp   = "vpp"
 	singleTopoContainerNginx = "nginx"
 
-	tapNameVpp  = "vppTap"
-	tapNameHost = "hostTap"
+	tapInterfaceName = "hst_tap_host"
 )
 
 type NoTopoSuite struct {
@@ -13,19 +12,9 @@
 }
 
 func (s *NoTopoSuite) SetupSuite() {
+	s.loadNetworkTopology("tap")
+
 	s.loadContainerTopology("single")
-
-	s.addresser = NewAddresser(&s.HstSuite)
-
-	var vppTapDevConfig = NetDevConfig{"name": tapNameVpp}
-	vppTap, _ := NewTap(vppTapDevConfig, s.addresser)
-
-	var hostTapDevConfig = NetDevConfig{"name": tapNameHost}
-	hostTap, _ := NewTap(hostTapDevConfig, s.addresser)
-
-	s.netInterfaces = make(map[string]NetInterface)
-	s.netInterfaces[vppTap.Name()] = &vppTap
-	s.netInterfaces[hostTap.Name()] = &hostTap
 }
 
 func (s *NoTopoSuite) SetupTest() {
@@ -43,8 +32,7 @@
 	vpp, _ := container.newVppInstance(startupConfig)
 	vpp.start()
 
-	vppTapAddress := s.netInterfaces[tapNameVpp].AddressWithPrefix()
-	hostTapAddress := s.netInterfaces[tapNameHost].IP4AddressWithPrefix()
+	tapInterface := s.netInterfaces[tapInterfaceName]
 
-	vpp.createTap("tap0", hostTapAddress, vppTapAddress)
+	vpp.createTap(1, tapInterface)
 }
diff --git a/extras/hs-test/topo-network/tap.yaml b/extras/hs-test/topo-network/tap.yaml
index 6f04132..26481de 100644
--- a/extras/hs-test/topo-network/tap.yaml
+++ b/extras/hs-test/topo-network/tap.yaml
@@ -1,4 +1,10 @@
 ---
 devices:
-  - name: "tap0"
+  - name: "hst_tap_host"
     type: "tap"
+    ip4:
+      network: 1
+    peer:
+      name: ""
+      ip4:
+        network: 1
diff --git a/extras/hs-test/vppinstance.go b/extras/hs-test/vppinstance.go
index 2ecb4de..29b86d5 100644
--- a/extras/hs-test/vppinstance.go
+++ b/extras/hs-test/vppinstance.go
@@ -172,10 +172,8 @@
 }
 
 func (vpp *VppInstance) createAfPacket(
-	netInterface NetInterface,
+	veth *NetInterface,
 ) (interface_types.InterfaceIndex, error) {
-	veth := netInterface.(*NetworkInterfaceVeth)
-
 	createReq := &af_packet.AfPacketCreateV2{
 		UseRandomHwAddr: true,
 		HostIfName:      veth.Name(),
@@ -206,7 +204,7 @@
 	if veth.AddressWithPrefix() == (AddressWithPrefix{}) {
 		var err error
 		var ip4Address string
-		if ip4Address, err = veth.addresser.NewIp4Address(veth.peerNetworkNumber); err == nil {
+		if ip4Address, err = veth.addresser.NewIp4Address(veth.Peer().networkNumber); err == nil {
 			veth.SetAddress(ip4Address)
 		} else {
 			return 0, err
@@ -255,15 +253,15 @@
 }
 
 func (vpp *VppInstance) createTap(
-	hostInterfaceName string,
-	hostIp4Address IP4AddressWithPrefix,
-	vppIp4Address AddressWithPrefix,
+	id uint32,
+	tap *NetInterface,
 ) error {
 	createTapReq := &tapv2.TapCreateV2{
+		ID:               id,
 		HostIfNameSet:    true,
-		HostIfName:       hostInterfaceName,
+		HostIfName:       tap.Name(),
 		HostIP4PrefixSet: true,
-		HostIP4Prefix:    hostIp4Address,
+		HostIP4Prefix:    tap.IP4AddressWithPrefix(),
 	}
 	createTapReply := &tapv2.TapCreateV2Reply{}
 
@@ -276,7 +274,7 @@
 	addAddressReq := &interfaces.SwInterfaceAddDelAddress{
 		IsAdd:     true,
 		SwIfIndex: createTapReply.SwIfIndex,
-		Prefix:    vppIp4Address,
+		Prefix:    tap.Peer().AddressWithPrefix(),
 	}
 	addAddressReply := &interfaces.SwInterfaceAddDelAddressReply{}