/
endpoint.go
156 lines (135 loc) · 4.69 KB
/
endpoint.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package links
import (
"context"
"fmt"
"net"
"github.com/containernetworking/plugins/pkg/ns"
log "github.com/sirupsen/logrus"
"github.com/vishvananda/netlink"
)
const (
// containerlab's reserved OUI.
ClabOUI = "aa:c1:ab"
)
// Endpoint is the interface that all endpoint types implement.
// Endpoints like bridge, host, veth and macvlan are the types implementing this interface.
type Endpoint interface {
GetNode() Node
GetIfaceName() string
GetRandIfaceName() string
GetMac() net.HardwareAddr
String() string
// GetLink retrieves the link that the endpoint is assigned to
GetLink() Link
// Verify verifies that the endpoint is valid and can be deployed
Verify(context.Context, *VerifyLinkParams) error
// HasSameNodeAndInterface returns true if an endpoint that implements this interface
// has the same node and interface name as the given endpoint.
HasSameNodeAndInterface(ept Endpoint) bool
Remove(context.Context) error
// Deploy deploys the endpoint by calling the Deploy method of the link it is assigned to
// and passing the endpoint as an argument so that the link that consists of A and B endpoints
// can deploy them independently.
Deploy(context.Context) error
// IsNodeless returns true for the endpoints that has no explicit node defined in the topology.
// E.g. host endpoints, mgmt bridge endpoints.
// Because there is no node that would deploy this side of the link they should be deployed along
// with the A side of the veth link.
IsNodeless() bool
}
// EndpointGeneric is the generic endpoint struct that is used by all endpoint types.
type EndpointGeneric struct {
Node Node
IfaceName string
// Link is the link this endpoint belongs to.
Link Link
MAC net.HardwareAddr
randName string
}
func NewEndpointGeneric(node Node, iface string, link Link) *EndpointGeneric {
return &EndpointGeneric{
Node: node,
IfaceName: iface,
// random name is generated for the endpoint to avoid name collisions
// when it is first deployed in the root namespace
randName: genRandomIfName(),
Link: link,
}
}
func (e *EndpointGeneric) GetRandIfaceName() string {
return e.randName
}
func (e *EndpointGeneric) GetIfaceName() string {
return e.IfaceName
}
func (e *EndpointGeneric) GetMac() net.HardwareAddr {
return e.MAC
}
func (e *EndpointGeneric) GetLink() Link {
return e.Link
}
func (e *EndpointGeneric) GetNode() Node {
return e.Node
}
func (e *EndpointGeneric) Remove(ctx context.Context) error {
return e.GetNode().ExecFunction(ctx, func(n ns.NetNS) error {
brSideEp, err := netlink.LinkByName(e.GetIfaceName())
_, notfound := err.(netlink.LinkNotFoundError)
switch {
case notfound:
// interface is not present, all good
return nil
case err != nil:
return err
}
log.Debugf("Removing interface %q from namespace %q", e.GetIfaceName(), e.GetNode().GetShortName())
return netlink.LinkDel(brSideEp)
})
}
// HasSameNodeAndInterface returns true if the given endpoint has the same node and interface name
// as the `ept` endpoint.
func (e *EndpointGeneric) HasSameNodeAndInterface(ept Endpoint) bool {
return e.Node == ept.GetNode() && e.IfaceName == ept.GetIfaceName()
}
func (e *EndpointGeneric) String() string {
return fmt.Sprintf("%s:%s", e.Node.GetShortName(), e.IfaceName)
}
// CheckEndpointUniqueness checks that the given endpoint appears only once for the node
// it is assigned to.
func CheckEndpointUniqueness(e Endpoint) error {
for _, ept := range e.GetNode().GetEndpoints() {
if e == ept {
// since node contains all endpoints including the one we are checking
// we skip it
continue
}
// if `e` has the same node and interface name as `ept` then we have a duplicate
if e.HasSameNodeAndInterface(ept) {
return fmt.Errorf("duplicate endpoint %s", e)
}
}
return nil
}
// CheckEndpointExists checks that a certain
// interface exists in the network namespace of the given node.
func CheckEndpointExists(ctx context.Context, e Endpoint) error {
err := CheckEndpointDoesNotExistYet(ctx, e)
if err == nil {
return fmt.Errorf("interface %q does not exist", e.String())
}
return nil
}
// CheckEndpointDoesNotExistYet verifies that the interface referenced in the
// provided endpoint does not yet exist in the referenced node.
func CheckEndpointDoesNotExistYet(ctx context.Context, e Endpoint) error {
return e.GetNode().ExecFunction(ctx, func(_ ns.NetNS) error {
// we expect a netlink.LinkNotFoundError when querying for
// the interface with the given endpoints name
var err error
_, err = netlink.LinkByName(e.GetIfaceName())
if _, notfound := err.(netlink.LinkNotFoundError); notfound {
return nil
}
return fmt.Errorf("interface %s is defined via topology but does already exist: %v", e.String(), err)
})
}