Browse code

multitenant sdn support; bump openshift-sdn/ovssubnet(74738c359b670c6e12435c1af10ee2802a4b0b64) vagrant instance updated to consume 2G

Rajat Chopra authored on 2015/06/19 06:19:45
Showing 58 changed files
... ...
@@ -1041,17 +1041,12 @@
1041 1041
 		{
1042 1042
 			"ImportPath": "github.com/openshift/openshift-sdn/ovssubnet",
1043 1043
 			"Comment": "v0.1-110-gcdd9955",
1044
-			"Rev": "cdd9955dc602abe8ef2d934a3c39417375c486c6"
1045
-		},
1046
-		{
1047
-			"ImportPath": "github.com/openshift/openshift-sdn/pkg/api",
1048
-			"Comment": "v0.1-110-gcdd9955",
1049
-			"Rev": "cdd9955dc602abe8ef2d934a3c39417375c486c6"
1044
+			"Rev": "74738c359b670c6e12435c1af10ee2802a4b0b64"
1050 1045
 		},
1051 1046
 		{
1052 1047
 			"ImportPath": "github.com/openshift/openshift-sdn/pkg/netutils",
1053 1048
 			"Comment": "v0.1-110-gcdd9955",
1054
-			"Rev": "cdd9955dc602abe8ef2d934a3c39417375c486c6"
1049
+			"Rev": "a15dc76526039229cfb0c93a4c0389d226550bc9"
1055 1050
 		},
1056 1051
 		{
1057 1052
 			"ImportPath": "github.com/openshift/source-to-image/pkg/api",
1058 1053
new file mode 100644
... ...
@@ -0,0 +1,66 @@
0
+package api
1
+
2
+type EventType string
3
+
4
+const (
5
+	Added   EventType = "ADDED"
6
+	Deleted EventType = "DELETED"
7
+)
8
+
9
+type SubnetRegistry interface {
10
+	InitSubnets() error
11
+	GetSubnets() (*[]Subnet, error)
12
+	GetSubnet(minion string) (*Subnet, error)
13
+	DeleteSubnet(minion string) error
14
+	CreateSubnet(sn string, sub *Subnet) error
15
+	WatchSubnets(receiver chan *SubnetEvent, stop chan bool) error
16
+
17
+	InitMinions() error
18
+	GetMinions() (*[]string, error)
19
+	CreateMinion(minion string, data string) error
20
+	WatchMinions(receiver chan *MinionEvent, stop chan bool) error
21
+
22
+	WriteNetworkConfig(network string, subnetLength uint) error
23
+	GetContainerNetwork() (string, error)
24
+	GetSubnetLength() (uint64, error)
25
+	CheckEtcdIsAlive(seconds uint64) bool
26
+
27
+	WatchNamespaces(receiver chan *NamespaceEvent, stop chan bool) error
28
+	WatchNetNamespaces(receiver chan *NetNamespaceEvent, stop chan bool) error
29
+	GetNetNamespaces() ([]NetNamespace, error)
30
+	GetNetNamespace(name string) (NetNamespace, error)
31
+	WriteNetNamespace(name string, id uint) error
32
+	DeleteNetNamespace(name string) error
33
+}
34
+
35
+type SubnetEvent struct {
36
+	Type   EventType
37
+	Minion string
38
+	Sub    Subnet
39
+}
40
+
41
+type MinionEvent struct {
42
+	Type   EventType
43
+	Minion string
44
+}
45
+
46
+type Subnet struct {
47
+	Minion string
48
+	Sub    string
49
+}
50
+
51
+type NetNamespace struct {
52
+	Name  string
53
+	NetID uint
54
+}
55
+
56
+type NetNamespaceEvent struct {
57
+	Type  EventType
58
+	Name  string
59
+	NetID uint
60
+}
61
+
62
+type NamespaceEvent struct {
63
+	Type EventType
64
+	Name string
65
+}
0 66
deleted file mode 100644
... ...
@@ -1,82 +0,0 @@
1
-#!/bin/bash
2
-set -ex
3
-
4
-lock_file=/var/lock/openshift-sdn.lock
5
-
6
-action=$1
7
-pod_namespace=$2
8
-pod_name=$3
9
-net_container=$4
10
-
11
-lockwrap() {
12
-    (
13
-    flock 200
14
-    "$@"
15
-    ) 200>${lock_file}
16
-}
17
-
18
-Init() {
19
-    true
20
-}
21
-
22
-Setup() {
23
-    source /etc/openshift-sdn/config.env
24
-    cluster_subnet=${OPENSHIFT_CLUSTER_SUBNET}
25
-    tap_ip=${OPENSHIFT_SDN_TAP1_ADDR}
26
-
27
-    pid=$(docker inspect --format "{{.State.Pid}}" ${net_container})
28
-    network_mode=$(docker inspect --format "{{.HostConfig.NetworkMode}}" ${net_container})
29
-    if [ "${network_mode}" == "host" ]; then
30
-      # quit, nothing for the SDN here
31
-      exit 0
32
-    fi
33
-    ipaddr=$(docker inspect --format "{{.NetworkSettings.IPAddress}}" ${net_container})
34
-    new_ip=$ipaddr
35
-    ipaddr_sub=$(docker inspect --format "{{.NetworkSettings.IPPrefixLen}}" ${net_container})
36
-    docker_gateway=$(docker inspect --format "{{.NetworkSettings.Gateway}}" ${net_container})
37
-    veth_ifindex=$(nsenter -n -t $pid -- ethtool -S eth0 | sed -n -e 's/.*peer_ifindex: //p')
38
-    veth_host=$(ip link show | sed -ne "s/^$veth_ifindex: \([^:]*\).*/\1/p")
39
-
40
-    brctl delif lbr0 $veth_host
41
-    ovs-vsctl add-port br0 ${veth_host} 
42
-    ovs_port=$(ovs-ofctl -O OpenFlow13 dump-ports-desc br0  | grep ${veth_host} | cut -d "(" -f 1 | tr -d ' ')
43
-    ovs-ofctl -O OpenFlow13 add-flow br0 "table=0,cookie=0x${ovs_port},priority=100,ip,nw_dst=${new_ip},actions=output:${ovs_port}"
44
-    ovs-ofctl -O OpenFlow13 add-flow br0 "table=0,cookie=0x${ovs_port},priority=100,arp,nw_dst=${new_ip},actions=output:${ovs_port}"
45
-
46
-    add_subnet_route="ip route add ${cluster_subnet} dev eth0 proto kernel scope link src $ipaddr"
47
-    nsenter -n -t $pid -- $add_subnet_route
48
-}
49
-
50
-Teardown() {
51
-    source /etc/openshift-sdn/config.env
52
-    cluster_subnet=${OPENSHIFT_CLUSTER_SUBNET}
53
-    tap_ip=${OPENSHIFT_SDN_TAP1_ADDR}
54
-
55
-    pid=$(docker inspect --format "{{.State.Pid}}" ${net_container})
56
-    network_mode=$(docker inspect --format "{{.HostConfig.NetworkMode}}" ${net_container})
57
-    if [ "${network_mode}" == "host" ]; then
58
-      # quit, nothing for the SDN here
59
-      exit 0
60
-    fi
61
-    veth_ifindex=$(nsenter -n -t $pid -- ethtool -S eth0 | sed -n -e 's/.*peer_ifindex: //p')
62
-    veth_host=$(ip link show | sed -ne "s/^$veth_ifindex: \([^:]*\).*/\1/p")
63
-    ovs_port=$(ovs-ofctl -O OpenFlow13 dump-ports-desc br0  | grep ${veth_host} | cut -d "(" -f 1 | tr -d ' ')
64
-    ovs-vsctl del-port $veth_host
65
-    ovs-ofctl -O OpenFlow13 del-flows br0 "table=0,cookie=0x${ovs_port}/0xffffffff"
66
-}
67
-
68
-case "$action" in
69
-    init)
70
-	lockwrap Init
71
-	;;
72
-    setup)
73
-	lockwrap Setup
74
-	;;
75
-    teardown)
76
-	lockwrap Teardown
77
-	;;
78
-    *)
79
-        echo "Bad input: $@"
80
-        exit 1
81
-esac
82
-
83 1
deleted file mode 100644
... ...
@@ -1,120 +0,0 @@
1
-#!/bin/bash
2
-
3
-set -ex
4
-
5
-lock_file=/var/lock/openshift-sdn.lock
6
-subnet_gateway=$1
7
-subnet=$2
8
-cluster_subnet=$3
9
-subnet_mask_len=$4
10
-tun_gateway=$5
11
-printf 'Container network is "%s"; local host has subnet "%s" and gateway "%s".\n' "${cluster_subnet}" "${subnet}" "${subnet_gateway}"
12
-TUN=tun0
13
-
14
-# Synchronize code execution with a file lock.
15
-function lockwrap() {
16
-    (
17
-    flock 200
18
-    "$@"
19
-    ) 200>${lock_file}
20
-}
21
-
22
-function setup_required() {
23
-    ip=$(echo `ip a s lbr0 2>/dev/null|awk '/inet / {print $2}'`)
24
-    if [ "$ip" != "${subnet_gateway}/${subnet_mask_len}" ]; then
25
-        return 0
26
-    fi
27
-    if ! grep -q lbr0 /run/openshift-sdn/docker-network; then
28
-        return 0
29
-    fi
30
-    return 1
31
-}
32
-
33
-function setup() {
34
-    # clear config file
35
-    rm -f /etc/openshift-sdn/config.env
36
-
37
-    ## openvswitch
38
-    ovs-vsctl del-br br0 || true
39
-    ovs-vsctl add-br br0 -- set Bridge br0 fail-mode=secure
40
-    ovs-vsctl set bridge br0 protocols=OpenFlow13
41
-    ovs-vsctl del-port br0 vxlan0 || true
42
-    ovs-vsctl add-port br0 vxlan0 -- set Interface vxlan0 type=vxlan options:remote_ip="flow" options:key="flow" ofport_request=1
43
-    ovs-vsctl add-port br0 ${TUN} -- set Interface ${TUN} type=internal ofport_request=2
44
-
45
-    ip link del vlinuxbr || true
46
-    ip link add vlinuxbr type veth peer name vovsbr
47
-    ip link set vlinuxbr up
48
-    ip link set vovsbr up
49
-    ip link set vlinuxbr txqueuelen 0
50
-    ip link set vovsbr txqueuelen 0
51
-
52
-    ovs-vsctl del-port br0 vovsbr || true
53
-    ovs-vsctl add-port br0 vovsbr -- set Interface vovsbr ofport_request=9
54
-
55
-    ## linux bridge
56
-    ip link set lbr0 down || true
57
-    brctl delbr lbr0 || true
58
-    brctl addbr lbr0
59
-    ip addr add ${subnet_gateway}/${subnet_mask_len} dev lbr0
60
-    ip link set lbr0 up
61
-    brctl addif lbr0 vlinuxbr
62
-
63
-    # setup tun address
64
-    ip addr add ${tun_gateway}/${subnet_mask_len} dev ${TUN}
65
-    ip link set ${TUN} up
66
-    ip route add ${cluster_subnet} dev ${TUN} proto kernel scope link
67
-
68
-    ## iptables
69
-    iptables -t nat -D POSTROUTING -s ${cluster_subnet} ! -d ${cluster_subnet} -j MASQUERADE || true
70
-    iptables -t nat -A POSTROUTING -s ${cluster_subnet} ! -d ${cluster_subnet} -j MASQUERADE
71
-    iptables -D INPUT -p udp -m multiport --dports 4789 -m comment --comment "001 vxlan incoming" -j ACCEPT || true
72
-    iptables -D INPUT -i ${TUN} -m comment --comment "traffic from docker for internet" -j ACCEPT || true
73
-    lineno=$(iptables -nvL INPUT --line-numbers | grep "state RELATED,ESTABLISHED" | awk '{print $1}')
74
-    iptables -I INPUT $lineno -p udp -m multiport --dports 4789 -m comment --comment "001 vxlan incoming" -j ACCEPT
75
-    iptables -I INPUT $((lineno+1)) -i ${TUN} -m comment --comment "traffic from docker for internet" -j ACCEPT
76
-    fwd_lineno=$(iptables -nvL FORWARD --line-numbers | grep "reject-with icmp-host-prohibited" | tail -n 1 | awk '{print $1}')
77
-    iptables -I FORWARD $fwd_lineno -d ${cluster_subnet} -j ACCEPT
78
-    iptables -I FORWARD $fwd_lineno -s ${cluster_subnet} -j ACCEPT
79
-
80
-    ## docker
81
-    if [[ -z "${DOCKER_NETWORK_OPTIONS}" ]]
82
-    then
83
-        DOCKER_NETWORK_OPTIONS='-b=lbr0 --mtu=1450'
84
-    fi
85
-
86
-    mkdir -p /run/openshift-sdn
87
-    cat <<EOF > /run/openshift-sdn/docker-network
88
-# This file has been modified by openshift-sdn. Please modify the
89
-# DOCKER_NETWORK_OPTIONS variable in /etc/sysconfig/openshift-node if this
90
-# is an integrated install or /etc/sysconfig/openshift-sdn-node if this is a
91
-# standalone install.
92
-
93
-DOCKER_NETWORK_OPTIONS='${DOCKER_NETWORK_OPTIONS}'
94
-EOF
95
-
96
-    systemctl daemon-reload
97
-    systemctl restart docker.service
98
-
99
-    # disable iptables for lbr0
100
-    # for kernel version 3.18+, module br_netfilter needs to be loaded upfront
101
-    # for older ones, br_netfilter may not exist, but is covered by bridge (bridge-utils)
102
-    modprobe br_netfilter || true 
103
-    sysctl -w net.bridge.bridge-nf-call-iptables=0
104
-
105
-    # delete the subnet routing entry created because of lbr0
106
-    ip route del ${subnet} dev lbr0 proto kernel scope link src ${subnet_gateway} || true
107
-
108
-    mkdir -p /etc/openshift-sdn
109
-    echo "export OPENSHIFT_SDN_TAP1_ADDR=${tun_gateway}" >& "/etc/openshift-sdn/config.env"
110
-    echo "export OPENSHIFT_CLUSTER_SUBNET=${cluster_subnet}" >> "/etc/openshift-sdn/config.env"
111
-}
112
-
113
-set +e
114
-if ! setup_required; then
115
-    echo "SDN setup not required."
116
-    exit 140
117
-fi
118
-set -e
119
-
120
-lockwrap setup
121 1
deleted file mode 100644
... ...
@@ -1,68 +0,0 @@
1
-#!/bin/bash
2
-
3
-set -ex
4
-
5
-subnet_gateway=$1
6
-subnet=$2
7
-container_network=$3
8
-subnet_mask_len=$4
9
-printf 'Container network is "%s"; local host has subnet "%s" and gateway "%s".\n' "${container_network}" "${subnet}" "${subnet_gateway}"
10
-
11
-## openvswitch
12
-ovs-vsctl del-br br0 || true
13
-ovs-vsctl add-br br0 -- set Bridge br0 fail-mode=secure
14
-ovs-vsctl set bridge br0 protocols=OpenFlow13
15
-ovs-vsctl del-port br0 vxlan0 || true
16
-ovs-vsctl add-port br0 vxlan0 -- set Interface vxlan0 type=vxlan options:remote_ip="flow" options:key="flow" ofport_request=10
17
-ip link del vlinuxbr || true
18
-ip link add vlinuxbr type veth peer name vovsbr
19
-ip link set vlinuxbr up
20
-ip link set vovsbr up
21
-ip link set vlinuxbr txqueuelen 0
22
-ip link set vovsbr txqueuelen 0
23
-
24
-ovs-vsctl del-port br0 vovsbr || true
25
-ovs-vsctl add-port br0 vovsbr -- set Interface vovsbr ofport_request=9
26
-
27
-## linux bridge
28
-ip link set lbr0 down || true
29
-brctl delbr lbr0 || true
30
-brctl addbr lbr0
31
-ip addr add ${subnet_gateway}/${subnet_mask_len} dev lbr0
32
-ip link set lbr0 up
33
-brctl addif lbr0 vlinuxbr
34
-ip route del ${subnet} dev lbr0 proto kernel scope link src ${subnet_gateway} || true
35
-ip route add ${container_network} dev lbr0 proto kernel scope link src ${subnet_gateway}
36
-
37
-
38
-## iptables
39
-iptables -t nat -D POSTROUTING -s ${container_network} ! -d ${container_network} -j MASQUERADE || true
40
-iptables -t nat -A POSTROUTING -s ${container_network} ! -d ${container_network} -j MASQUERADE
41
-iptables -D INPUT -p udp -m multiport --dports 4789 -m comment --comment "001 vxlan incoming" -j ACCEPT || true
42
-iptables -D INPUT -i lbr0 -m comment --comment "traffic from docker" -j ACCEPT || true
43
-lineno=$(iptables -nvL INPUT --line-numbers | grep "state RELATED,ESTABLISHED" | awk '{print $1}')
44
-iptables -I INPUT $lineno -p udp -m multiport --dports 4789 -m comment --comment "001 vxlan incoming" -j ACCEPT
45
-iptables -I INPUT $((lineno+1)) -i lbr0 -m comment --comment "traffic from docker" -j ACCEPT
46
-fwd_lineno=$(iptables -nvL FORWARD --line-numbers | grep "reject-with icmp-host-prohibited" | tail -n 1 | awk '{print $1}')
47
-iptables -I FORWARD $fwd_lineno -d ${container_network} -j ACCEPT
48
-iptables -I FORWARD $fwd_lineno -s ${container_network} -j ACCEPT
49
-
50
-
51
-## docker
52
-if [[ -z "${DOCKER_NETWORK_OPTIONS}" ]]
53
-then
54
-    DOCKER_NETWORK_OPTIONS='-b=lbr0 --mtu=1450'
55
-fi
56
-
57
-mkdir -p /run/openshift-sdn
58
-cat <<EOF > /run/openshift-sdn/docker-network
59
-# This file has been modified by openshift-sdn. Please modify the
60
-# DOCKER_NETWORK_OPTIONS variable in /etc/sysconfig/openshift-node if this
61
-# is an integrated install or /etc/sysconfig/openshift-sdn-node if this is a
62
-# standalone install.
63
-
64
-DOCKER_NETWORK_OPTIONS='${DOCKER_NETWORK_OPTIONS}'
65
-EOF
66
-
67
-systemctl daemon-reload
68
-systemctl restart docker.service
... ...
@@ -7,12 +7,17 @@ import (
7 7
 	"net"
8 8
 	"time"
9 9
 
10
+	"github.com/openshift/openshift-sdn/ovssubnet/api"
10 11
 	"github.com/openshift/openshift-sdn/ovssubnet/controller/kube"
11 12
 	"github.com/openshift/openshift-sdn/ovssubnet/controller/lbr"
12
-	"github.com/openshift/openshift-sdn/pkg/api"
13
+	"github.com/openshift/openshift-sdn/ovssubnet/controller/multitenant"
13 14
 	"github.com/openshift/openshift-sdn/pkg/netutils"
14 15
 )
15 16
 
17
+const (
18
+	MaxUint = ^uint(0)
19
+)
20
+
16 21
 type OvsController struct {
17 22
 	subnetRegistry  api.SubnetRegistry
18 23
 	localIP         string
... ...
@@ -22,6 +27,8 @@ type OvsController struct {
22 22
 	sig             chan struct{}
23 23
 	ready           chan struct{}
24 24
 	flowController  FlowController
25
+	VnidMap         map[string]uint
26
+	netIDManager    *netutils.NetIDAllocator
25 27
 }
26 28
 
27 29
 type FlowController interface {
... ...
@@ -38,6 +45,14 @@ func NewKubeController(sub api.SubnetRegistry, hostname string, selfIP string, r
38 38
 	return kubeController, err
39 39
 }
40 40
 
41
+func NewMultitenantController(sub api.SubnetRegistry, hostname string, selfIP string, ready chan struct{}) (*OvsController, error) {
42
+	mtController, err := NewController(sub, hostname, selfIP, ready)
43
+	if err == nil {
44
+		mtController.flowController = multitenant.NewFlowController()
45
+	}
46
+	return mtController, err
47
+}
48
+
41 49
 func NewDefaultController(sub api.SubnetRegistry, hostname string, selfIP string, ready chan struct{}) (*OvsController, error) {
42 50
 	defaultController, err := NewController(sub, hostname, selfIP, ready)
43 51
 	if err == nil {
... ...
@@ -70,6 +85,7 @@ func NewController(sub api.SubnetRegistry, hostname string, selfIP string, ready
70 70
 		hostName:        hostname,
71 71
 		localSubnet:     nil,
72 72
 		subnetAllocator: nil,
73
+		VnidMap:         make(map[string]uint),
73 74
 		sig:             make(chan struct{}),
74 75
 		ready:           ready,
75 76
 	}, nil
... ...
@@ -116,10 +132,66 @@ func (oc *OvsController) StartMaster(sync bool, containerNetwork string, contain
116 116
 		log.Warningf("Error initializing existing minions: %v", err)
117 117
 		// no worry, we can still keep watching it.
118 118
 	}
119
+	if _, is_mt := oc.flowController.(*multitenant.FlowController); is_mt {
120
+		nets, err := oc.subnetRegistry.GetNetNamespaces()
121
+		if err != nil {
122
+			return err
123
+		}
124
+		inUse := make([]uint, 0)
125
+		for _, net := range nets {
126
+			inUse = append(inUse, net.NetID)
127
+			oc.VnidMap[net.Name] = net.NetID
128
+		}
129
+		oc.netIDManager, err = netutils.NewNetIDAllocator(10, MaxUint, inUse)
130
+		if err != nil {
131
+			return err
132
+		}
133
+		go oc.watchNetworks()
134
+	}
119 135
 	go oc.watchMinions()
120 136
 	return nil
121 137
 }
122 138
 
139
+func (oc *OvsController) watchNetworks() {
140
+	nsevent := make(chan *api.NamespaceEvent)
141
+	stop := make(chan bool)
142
+	go oc.subnetRegistry.WatchNamespaces(nsevent, stop)
143
+	for {
144
+		select {
145
+		case ev := <-nsevent:
146
+			switch ev.Type {
147
+			case api.Added:
148
+				_, err := oc.subnetRegistry.GetNetNamespace(ev.Name)
149
+				if err != nil {
150
+					netid, err := oc.netIDManager.GetNetID()
151
+					if err != nil {
152
+						log.Error("Error getting new network IDS: %v", err)
153
+						continue
154
+					}
155
+					err = oc.subnetRegistry.WriteNetNamespace(ev.Name, netid)
156
+					if err != nil {
157
+						log.Error("Error writing new network ID: %v", err)
158
+						continue
159
+					}
160
+					oc.VnidMap[ev.Name] = netid
161
+				}
162
+			case api.Deleted:
163
+				err := oc.subnetRegistry.DeleteNetNamespace(ev.Name)
164
+				if err != nil {
165
+					log.Error("Error while deleting Net Id: %v", err)
166
+				}
167
+				netid := oc.VnidMap[ev.Name]
168
+				oc.netIDManager.ReleaseNetID(netid)
169
+				delete(oc.VnidMap, ev.Name)
170
+			}
171
+		case <-oc.sig:
172
+			log.Error("Signal received. Stopping watching of minions.")
173
+			stop <- true
174
+			return
175
+		}
176
+	}
177
+}
178
+
123 179
 func (oc *OvsController) ServeExistingMinions() error {
124 180
 	minions, err := oc.subnetRegistry.GetMinions()
125 181
 	if err != nil {
... ...
@@ -226,6 +298,16 @@ func (oc *OvsController) StartNode(sync, skipsetup bool) error {
226 226
 	for _, s := range *subnets {
227 227
 		oc.flowController.AddOFRules(s.Minion, s.Sub, oc.localIP)
228 228
 	}
229
+	if _, ok := oc.flowController.(*multitenant.FlowController); ok {
230
+		nslist, err := oc.subnetRegistry.GetNetNamespaces()
231
+		if err != nil {
232
+			return err
233
+		}
234
+		for _, ns := range nslist {
235
+			oc.VnidMap[ns.Name] = ns.NetID
236
+		}
237
+		go oc.watchVnids()
238
+	}
229 239
 	go oc.watchCluster()
230 240
 
231 241
 	if oc.ready != nil {
... ...
@@ -235,6 +317,27 @@ func (oc *OvsController) StartNode(sync, skipsetup bool) error {
235 235
 	return err
236 236
 }
237 237
 
238
+func (oc *OvsController) watchVnids() {
239
+	netNsEvent := make(chan *api.NetNamespaceEvent)
240
+	stop := make(chan bool)
241
+	go oc.subnetRegistry.WatchNetNamespaces(netNsEvent, stop)
242
+	for {
243
+		select {
244
+		case ev := <-netNsEvent:
245
+			switch ev.Type {
246
+			case api.Added:
247
+				oc.VnidMap[ev.Name] = ev.NetID
248
+			case api.Deleted:
249
+				delete(oc.VnidMap, ev.Name)
250
+			}
251
+		case <-oc.sig:
252
+			log.Error("Signal received. Stopping watching of NetNamespaces.")
253
+			stop <- true
254
+			return
255
+		}
256
+	}
257
+}
258
+
238 259
 func (oc *OvsController) initSelfSubnet() error {
239 260
 	// get subnet for self
240 261
 	for {
241 262
new file mode 100644
... ...
@@ -0,0 +1,82 @@
0
+#!/bin/bash
1
+set -ex
2
+
3
+lock_file=/var/lock/openshift-sdn.lock
4
+
5
+action=$1
6
+pod_namespace=$2
7
+pod_name=$3
8
+net_container=$4
9
+
10
+lockwrap() {
11
+    (
12
+    flock 200
13
+    "$@"
14
+    ) 200>${lock_file}
15
+}
16
+
17
+Init() {
18
+    true
19
+}
20
+
21
+Setup() {
22
+    source /etc/openshift-sdn/config.env
23
+    cluster_subnet=${OPENSHIFT_CLUSTER_SUBNET}
24
+    tap_ip=${OPENSHIFT_SDN_TAP1_ADDR}
25
+
26
+    pid=$(docker inspect --format "{{.State.Pid}}" ${net_container})
27
+    network_mode=$(docker inspect --format "{{.HostConfig.NetworkMode}}" ${net_container})
28
+    if [ "${network_mode}" == "host" ]; then
29
+      # quit, nothing for the SDN here
30
+      exit 0
31
+    fi
32
+    ipaddr=$(docker inspect --format "{{.NetworkSettings.IPAddress}}" ${net_container})
33
+    new_ip=$ipaddr
34
+    ipaddr_sub=$(docker inspect --format "{{.NetworkSettings.IPPrefixLen}}" ${net_container})
35
+    docker_gateway=$(docker inspect --format "{{.NetworkSettings.Gateway}}" ${net_container})
36
+    veth_ifindex=$(nsenter -n -t $pid -- ethtool -S eth0 | sed -n -e 's/.*peer_ifindex: //p')
37
+    veth_host=$(ip link show | sed -ne "s/^$veth_ifindex: \([^:]*\).*/\1/p")
38
+
39
+    brctl delif lbr0 $veth_host
40
+    ovs-vsctl add-port br0 ${veth_host} 
41
+    ovs_port=$(ovs-ofctl -O OpenFlow13 dump-ports-desc br0  | grep ${veth_host} | cut -d "(" -f 1 | tr -d ' ')
42
+    ovs-ofctl -O OpenFlow13 add-flow br0 "table=0,cookie=0x${ovs_port},priority=100,ip,nw_dst=${new_ip},actions=output:${ovs_port}"
43
+    ovs-ofctl -O OpenFlow13 add-flow br0 "table=0,cookie=0x${ovs_port},priority=100,arp,nw_dst=${new_ip},actions=output:${ovs_port}"
44
+
45
+    add_subnet_route="ip route add ${cluster_subnet} dev eth0 proto kernel scope link src $ipaddr"
46
+    nsenter -n -t $pid -- $add_subnet_route
47
+}
48
+
49
+Teardown() {
50
+    source /etc/openshift-sdn/config.env
51
+    cluster_subnet=${OPENSHIFT_CLUSTER_SUBNET}
52
+    tap_ip=${OPENSHIFT_SDN_TAP1_ADDR}
53
+
54
+    pid=$(docker inspect --format "{{.State.Pid}}" ${net_container})
55
+    network_mode=$(docker inspect --format "{{.HostConfig.NetworkMode}}" ${net_container})
56
+    if [ "${network_mode}" == "host" ]; then
57
+      # quit, nothing for the SDN here
58
+      exit 0
59
+    fi
60
+    veth_ifindex=$(nsenter -n -t $pid -- ethtool -S eth0 | sed -n -e 's/.*peer_ifindex: //p')
61
+    veth_host=$(ip link show | sed -ne "s/^$veth_ifindex: \([^:]*\).*/\1/p")
62
+    ovs_port=$(ovs-ofctl -O OpenFlow13 dump-ports-desc br0  | grep ${veth_host} | cut -d "(" -f 1 | tr -d ' ')
63
+    ovs-vsctl del-port $veth_host
64
+    ovs-ofctl -O OpenFlow13 del-flows br0 "table=0,cookie=0x${ovs_port}/0xffffffff"
65
+}
66
+
67
+case "$action" in
68
+    init)
69
+	lockwrap Init
70
+	;;
71
+    setup)
72
+	lockwrap Setup
73
+	;;
74
+    teardown)
75
+	lockwrap Teardown
76
+	;;
77
+    *)
78
+        echo "Bad input: $@"
79
+        exit 1
80
+esac
81
+
0 82
new file mode 100644
... ...
@@ -0,0 +1,120 @@
0
+#!/bin/bash
1
+
2
+set -ex
3
+
4
+lock_file=/var/lock/openshift-sdn.lock
5
+subnet_gateway=$1
6
+subnet=$2
7
+cluster_subnet=$3
8
+subnet_mask_len=$4
9
+tun_gateway=$5
10
+printf 'Container network is "%s"; local host has subnet "%s" and gateway "%s".\n' "${cluster_subnet}" "${subnet}" "${subnet_gateway}"
11
+TUN=tun0
12
+
13
+# Synchronize code execution with a file lock.
14
+function lockwrap() {
15
+    (
16
+    flock 200
17
+    "$@"
18
+    ) 200>${lock_file}
19
+}
20
+
21
+function setup_required() {
22
+    ip=$(echo `ip a s lbr0 2>/dev/null|awk '/inet / {print $2}'`)
23
+    if [ "$ip" != "${subnet_gateway}/${subnet_mask_len}" ]; then
24
+        return 0
25
+    fi
26
+    if ! grep -q lbr0 /run/openshift-sdn/docker-network; then
27
+        return 0
28
+    fi
29
+    return 1
30
+}
31
+
32
+function setup() {
33
+    # clear config file
34
+    rm -f /etc/openshift-sdn/config.env
35
+
36
+    ## openvswitch
37
+    ovs-vsctl del-br br0 || true
38
+    ovs-vsctl add-br br0 -- set Bridge br0 fail-mode=secure
39
+    ovs-vsctl set bridge br0 protocols=OpenFlow13
40
+    ovs-vsctl del-port br0 vxlan0 || true
41
+    ovs-vsctl add-port br0 vxlan0 -- set Interface vxlan0 type=vxlan options:remote_ip="flow" options:key="flow" ofport_request=1
42
+    ovs-vsctl add-port br0 ${TUN} -- set Interface ${TUN} type=internal ofport_request=2
43
+
44
+    ip link del vlinuxbr || true
45
+    ip link add vlinuxbr type veth peer name vovsbr
46
+    ip link set vlinuxbr up
47
+    ip link set vovsbr up
48
+    ip link set vlinuxbr txqueuelen 0
49
+    ip link set vovsbr txqueuelen 0
50
+
51
+    ovs-vsctl del-port br0 vovsbr || true
52
+    ovs-vsctl add-port br0 vovsbr -- set Interface vovsbr ofport_request=9
53
+
54
+    ## linux bridge
55
+    ip link set lbr0 down || true
56
+    brctl delbr lbr0 || true
57
+    brctl addbr lbr0
58
+    ip addr add ${subnet_gateway}/${subnet_mask_len} dev lbr0
59
+    ip link set lbr0 up
60
+    brctl addif lbr0 vlinuxbr
61
+
62
+    # setup tun address
63
+    ip addr add ${tun_gateway}/${subnet_mask_len} dev ${TUN}
64
+    ip link set ${TUN} up
65
+    ip route add ${cluster_subnet} dev ${TUN} proto kernel scope link
66
+
67
+    ## iptables
68
+    iptables -t nat -D POSTROUTING -s ${cluster_subnet} ! -d ${cluster_subnet} -j MASQUERADE || true
69
+    iptables -t nat -A POSTROUTING -s ${cluster_subnet} ! -d ${cluster_subnet} -j MASQUERADE
70
+    iptables -D INPUT -p udp -m multiport --dports 4789 -m comment --comment "001 vxlan incoming" -j ACCEPT || true
71
+    iptables -D INPUT -i ${TUN} -m comment --comment "traffic from docker for internet" -j ACCEPT || true
72
+    lineno=$(iptables -nvL INPUT --line-numbers | grep "state RELATED,ESTABLISHED" | awk '{print $1}')
73
+    iptables -I INPUT $lineno -p udp -m multiport --dports 4789 -m comment --comment "001 vxlan incoming" -j ACCEPT
74
+    iptables -I INPUT $((lineno+1)) -i ${TUN} -m comment --comment "traffic from docker for internet" -j ACCEPT
75
+    fwd_lineno=$(iptables -nvL FORWARD --line-numbers | grep "reject-with icmp-host-prohibited" | tail -n 1 | awk '{print $1}')
76
+    iptables -I FORWARD $fwd_lineno -d ${cluster_subnet} -j ACCEPT
77
+    iptables -I FORWARD $fwd_lineno -s ${cluster_subnet} -j ACCEPT
78
+
79
+    ## docker
80
+    if [[ -z "${DOCKER_NETWORK_OPTIONS}" ]]
81
+    then
82
+        DOCKER_NETWORK_OPTIONS='-b=lbr0 --mtu=1450'
83
+    fi
84
+
85
+    mkdir -p /run/openshift-sdn
86
+    cat <<EOF > /run/openshift-sdn/docker-network
87
+# This file has been modified by openshift-sdn. Please modify the
88
+# DOCKER_NETWORK_OPTIONS variable in /etc/sysconfig/openshift-node if this
89
+# is an integrated install or /etc/sysconfig/openshift-sdn-node if this is a
90
+# standalone install.
91
+
92
+DOCKER_NETWORK_OPTIONS='${DOCKER_NETWORK_OPTIONS}'
93
+EOF
94
+
95
+    systemctl daemon-reload
96
+    systemctl restart docker.service
97
+
98
+    # disable iptables for lbr0
99
+    # for kernel version 3.18+, module br_netfilter needs to be loaded upfront
100
+    # for older ones, br_netfilter may not exist, but is covered by bridge (bridge-utils)
101
+    modprobe br_netfilter || true 
102
+    sysctl -w net.bridge.bridge-nf-call-iptables=0
103
+
104
+    # delete the subnet routing entry created because of lbr0
105
+    ip route del ${subnet} dev lbr0 proto kernel scope link src ${subnet_gateway} || true
106
+
107
+    mkdir -p /etc/openshift-sdn
108
+    echo "export OPENSHIFT_SDN_TAP1_ADDR=${tun_gateway}" >& "/etc/openshift-sdn/config.env"
109
+    echo "export OPENSHIFT_CLUSTER_SUBNET=${cluster_subnet}" >> "/etc/openshift-sdn/config.env"
110
+}
111
+
112
+set +e
113
+if ! setup_required; then
114
+    echo "SDN setup not required."
115
+    exit 140
116
+fi
117
+set -e
118
+
119
+lockwrap setup
0 120
new file mode 100644
... ...
@@ -0,0 +1,68 @@
0
+#!/bin/bash
1
+
2
+set -ex
3
+
4
+subnet_gateway=$1
5
+subnet=$2
6
+container_network=$3
7
+subnet_mask_len=$4
8
+printf 'Container network is "%s"; local host has subnet "%s" and gateway "%s".\n' "${container_network}" "${subnet}" "${subnet_gateway}"
9
+
10
+## openvswitch
11
+ovs-vsctl del-br br0 || true
12
+ovs-vsctl add-br br0 -- set Bridge br0 fail-mode=secure
13
+ovs-vsctl set bridge br0 protocols=OpenFlow13
14
+ovs-vsctl del-port br0 vxlan0 || true
15
+ovs-vsctl add-port br0 vxlan0 -- set Interface vxlan0 type=vxlan options:remote_ip="flow" options:key="flow" ofport_request=10
16
+ip link del vlinuxbr || true
17
+ip link add vlinuxbr type veth peer name vovsbr
18
+ip link set vlinuxbr up
19
+ip link set vovsbr up
20
+ip link set vlinuxbr txqueuelen 0
21
+ip link set vovsbr txqueuelen 0
22
+
23
+ovs-vsctl del-port br0 vovsbr || true
24
+ovs-vsctl add-port br0 vovsbr -- set Interface vovsbr ofport_request=9
25
+
26
+## linux bridge
27
+ip link set lbr0 down || true
28
+brctl delbr lbr0 || true
29
+brctl addbr lbr0
30
+ip addr add ${subnet_gateway}/${subnet_mask_len} dev lbr0
31
+ip link set lbr0 up
32
+brctl addif lbr0 vlinuxbr
33
+ip route del ${subnet} dev lbr0 proto kernel scope link src ${subnet_gateway} || true
34
+ip route add ${container_network} dev lbr0 proto kernel scope link src ${subnet_gateway}
35
+
36
+
37
+## iptables
38
+iptables -t nat -D POSTROUTING -s ${container_network} ! -d ${container_network} -j MASQUERADE || true
39
+iptables -t nat -A POSTROUTING -s ${container_network} ! -d ${container_network} -j MASQUERADE
40
+iptables -D INPUT -p udp -m multiport --dports 4789 -m comment --comment "001 vxlan incoming" -j ACCEPT || true
41
+iptables -D INPUT -i lbr0 -m comment --comment "traffic from docker" -j ACCEPT || true
42
+lineno=$(iptables -nvL INPUT --line-numbers | grep "state RELATED,ESTABLISHED" | awk '{print $1}')
43
+iptables -I INPUT $lineno -p udp -m multiport --dports 4789 -m comment --comment "001 vxlan incoming" -j ACCEPT
44
+iptables -I INPUT $((lineno+1)) -i lbr0 -m comment --comment "traffic from docker" -j ACCEPT
45
+fwd_lineno=$(iptables -nvL FORWARD --line-numbers | grep "reject-with icmp-host-prohibited" | tail -n 1 | awk '{print $1}')
46
+iptables -I FORWARD $fwd_lineno -d ${container_network} -j ACCEPT
47
+iptables -I FORWARD $fwd_lineno -s ${container_network} -j ACCEPT
48
+
49
+
50
+## docker
51
+if [[ -z "${DOCKER_NETWORK_OPTIONS}" ]]
52
+then
53
+    DOCKER_NETWORK_OPTIONS='-b=lbr0 --mtu=1450'
54
+fi
55
+
56
+mkdir -p /run/openshift-sdn
57
+cat <<EOF > /run/openshift-sdn/docker-network
58
+# This file has been modified by openshift-sdn. Please modify the
59
+# DOCKER_NETWORK_OPTIONS variable in /etc/sysconfig/openshift-node if this
60
+# is an integrated install or /etc/sysconfig/openshift-sdn-node if this is a
61
+# standalone install.
62
+
63
+DOCKER_NETWORK_OPTIONS='${DOCKER_NETWORK_OPTIONS}'
64
+EOF
65
+
66
+systemctl daemon-reload
67
+systemctl restart docker.service
0 68
new file mode 100755
... ...
@@ -0,0 +1,89 @@
0
+#!/bin/bash
1
+set -ex
2
+
3
+lock_file=/var/lock/openshift-sdn.lock
4
+
5
+action=$1
6
+pod_namespace=$2
7
+pod_name=$3
8
+net_container=$4
9
+tenant_id=$5
10
+
11
+lockwrap() {
12
+    (
13
+    flock 200
14
+    "$@"
15
+    ) 200>${lock_file}
16
+}
17
+
18
+Init() {
19
+    true
20
+}
21
+
22
+Setup() {
23
+    source /etc/openshift-sdn/config.env
24
+    cluster_subnet=${OPENSHIFT_CLUSTER_SUBNET}
25
+
26
+    pid=$(docker inspect --format "{{.State.Pid}}" ${net_container})
27
+    network_mode=$(docker inspect --format "{{.HostConfig.NetworkMode}}" ${net_container})
28
+    if [ "${network_mode}" == "host" ]; then
29
+      # quit, nothing for the SDN here
30
+      exit 0
31
+    fi
32
+    ipaddr=$(docker inspect --format "{{.NetworkSettings.IPAddress}}" ${net_container})
33
+    veth_ifindex=$(nsenter -n -t $pid -- ethtool -S eth0 | sed -n -e 's/.*peer_ifindex: //p')
34
+    veth_host=$(ip link show | sed -ne "s/^$veth_ifindex: \([^:]*\).*/\1/p")
35
+
36
+    # If the caller didn't pass a tenant ID, we make one up based on
37
+    # the first two letters of the pod name.
38
+    # FIXME: remove this when it's no longer needed for testing
39
+    if [ -z "$tenant_id" ]; then
40
+       tenant_id=$(echo $pod_name | perl -ne 'print unpack("S", $_)')
41
+    fi
42
+
43
+    brctl delif lbr0 $veth_host
44
+    ovs-vsctl add-port br0 ${veth_host} 
45
+    ovs_port=$(ovs-ofctl -O OpenFlow13 dump-ports-desc br0  | grep ${veth_host} | cut -d "(" -f 1 | tr -d ' ')
46
+
47
+    ovs-ofctl -O OpenFlow13 add-flow br0 "table=3,cookie=0x${ovs_port},priority=100,in_port=${ovs_port},ip,nw_src=${ipaddr},actions=load:${tenant_id}->NXM_NX_REG0[],goto_table:4"
48
+    ovs-ofctl -O OpenFlow13 add-flow br0 "table=5,cookie=0x${ovs_port},priority=100,ip,nw_dst=${ipaddr},reg0=${tenant_id},actions=output:${ovs_port}"
49
+    if [ "${tenant_id}" == "0" ]; then
50
+      ovs-ofctl -O OpenFlow13 add-flow br0 "table=5,cookie=0x${ovs_port},priority=150,ip,nw_dst=${ipaddr},actions=output:${ovs_port}"
51
+    fi
52
+
53
+    add_subnet_route="ip route add ${cluster_subnet} dev eth0 proto kernel scope link src $ipaddr"
54
+    nsenter -n -t $pid -- $add_subnet_route
55
+}
56
+
57
+Teardown() {
58
+    source /etc/openshift-sdn/config.env
59
+
60
+    pid=$(docker inspect --format "{{.State.Pid}}" ${net_container})
61
+    network_mode=$(docker inspect --format "{{.HostConfig.NetworkMode}}" ${net_container})
62
+    if [ "${network_mode}" == "host" ]; then
63
+      # quit, nothing for the SDN here
64
+      exit 0
65
+    fi
66
+    veth_ifindex=$(nsenter -n -t $pid -- ethtool -S eth0 | sed -n -e 's/.*peer_ifindex: //p')
67
+    veth_host=$(ip link show | sed -ne "s/^$veth_ifindex: \([^:]*\).*/\1/p")
68
+    ovs_port=$(ovs-ofctl -O OpenFlow13 dump-ports-desc br0  | grep ${veth_host} | cut -d "(" -f 1 | tr -d ' ')
69
+    ovs-vsctl del-port $veth_host
70
+    ovs-ofctl -O OpenFlow13 del-flows br0 "table=3,cookie=0x${ovs_port}/0xffffffff"
71
+    ovs-ofctl -O OpenFlow13 del-flows br0 "table=5,cookie=0x${ovs_port}/0xffffffff"
72
+}
73
+
74
+case "$action" in
75
+    init)
76
+	lockwrap Init
77
+	;;
78
+    setup)
79
+	lockwrap Setup
80
+	;;
81
+    teardown)
82
+	lockwrap Teardown
83
+	;;
84
+    *)
85
+        echo "Bad input: $@"
86
+        exit 1
87
+esac
88
+
0 89
new file mode 100755
... ...
@@ -0,0 +1,154 @@
0
+#!/bin/bash
1
+
2
+set -ex
3
+
4
+lock_file=/var/lock/openshift-sdn.lock
5
+subnet_gateway=$1
6
+subnet=$2
7
+cluster_subnet=$3
8
+subnet_mask_len=$4
9
+tun_gateway=$5
10
+printf 'Container network is "%s"; local host has subnet "%s" and gateway "%s".\n' "${cluster_subnet}" "${subnet}" "${subnet_gateway}"
11
+TUN=tun0
12
+
13
+# Synchronize code execution with a file lock.
14
+function lockwrap() {
15
+    (
16
+    flock 200
17
+    "$@"
18
+    ) 200>${lock_file}
19
+}
20
+
21
+function setup_required() {
22
+    ip=$(echo `ip a s lbr0 2>/dev/null|awk '/inet / {print $2}'`)
23
+    if [ "$ip" != "${subnet_gateway}/${subnet_mask_len}" ]; then
24
+        return 0
25
+    fi
26
+    if ! grep -q lbr0 /run/openshift-sdn/docker-network; then
27
+        return 0
28
+    fi
29
+    return 1
30
+}
31
+
32
+function setup() {
33
+    # clear config file
34
+    rm -f /etc/openshift-sdn/config.env
35
+
36
+    ## openvswitch
37
+    ovs-vsctl del-br br0 || true
38
+    ovs-vsctl add-br br0 -- set Bridge br0 fail-mode=secure
39
+    ovs-vsctl set bridge br0 protocols=OpenFlow13
40
+    ovs-vsctl del-port br0 vxlan0 || true
41
+    ovs-vsctl add-port br0 vxlan0 -- set Interface vxlan0 type=vxlan options:remote_ip="flow" options:key="flow" ofport_request=1
42
+    ovs-vsctl add-port br0 ${TUN} -- set Interface ${TUN} type=internal ofport_request=2
43
+
44
+    ip link del vlinuxbr || true
45
+    ip link add vlinuxbr type veth peer name vovsbr
46
+    ip link set vlinuxbr up
47
+    ip link set vovsbr up
48
+    ip link set vlinuxbr txqueuelen 0
49
+    ip link set vovsbr txqueuelen 0
50
+
51
+    ovs-vsctl del-port br0 vovsbr || true
52
+    ovs-vsctl add-port br0 vovsbr -- set Interface vovsbr ofport_request=9
53
+
54
+    # Table 0; learn MAC addresses and continue with table 1
55
+    ovs-ofctl -O OpenFlow13 add-flow br0 "table=0, actions=learn(table=7, priority=200, hard_timeout=900, NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], load:NXM_NX_TUN_IPV4_SRC[]->NXM_NX_TUN_IPV4_DST[], output:NXM_OF_IN_PORT[]), goto_table:1"
56
+
57
+    # Table 1; initial dispatch
58
+    ovs-ofctl -O OpenFlow13 add-flow br0 "table=1, arp, actions=goto_table:7"
59
+    ovs-ofctl -O OpenFlow13 add-flow br0 "table=1, in_port=1, actions=goto_table:2"
60
+    ovs-ofctl -O OpenFlow13 add-flow br0 "table=1, in_port=2, actions=goto_table:4"
61
+    ovs-ofctl -O OpenFlow13 add-flow br0 "table=1, actions=goto_table:3"
62
+
63
+    # Table 2; incoming from vxlan
64
+    ovs-ofctl -O OpenFlow13 add-flow br0 "table=2, arp, actions=goto_table:7"
65
+    ovs-ofctl -O OpenFlow13 add-flow br0 "table=2, priority=200, ip, nw_dst=${subnet_gateway}, actions=output:2"
66
+    ovs-ofctl -O OpenFlow13 add-flow br0 "table=2, tun_id=0, actions=goto_table:4"
67
+    ovs-ofctl -O OpenFlow13 add-flow br0 "table=2, priority=100, ip, nw_dst=${subnet}, actions=move:NXM_NX_TUN_ID[0..31]->NXM_NX_REG0[], goto_table:5"
68
+
69
+    # Table 3; incoming from container; filled in by openshift-ovs-subnet
70
+    # But let incoming traffic from docker-only containers through (ingress on vovsbr)
71
+    ovs-ofctl -O OpenFlow13 add-flow br0 "table=3, cookie=0x9, in_port=9, ip, actions=goto_table:4"
72
+
73
+    # Table 4; general routing
74
+    ovs-ofctl -O OpenFlow13 add-flow br0 "table=4, priority=200, ip, nw_dst=${subnet_gateway}, actions=output:2"
75
+    ovs-ofctl -O OpenFlow13 add-flow br0 "table=4, priority=150, ip, nw_dst=${subnet}, actions=goto_table:5"
76
+    ovs-ofctl -O OpenFlow13 add-flow br0 "table=4, priority=100, ip, nw_dst=${cluster_subnet}, actions=goto_table:6"
77
+    ovs-ofctl -O OpenFlow13 add-flow br0 "table=4, priority=0, ip, actions=output:2"
78
+
79
+    # Table 5; to local container; mostly filled in by openshift-ovs-multitenant
80
+    ovs-ofctl -O OpenFlow13 add-flow br0 "table=5, priority=200, ip, reg0=0, actions=goto_table:7"
81
+
82
+    # Table 6; to remote container; filled in by multitenant.go
83
+
84
+    # Table 7; MAC dispatch / ARP, filled in by Table 0's learn() rule
85
+    # and with per-node vxlan ARP rules by multitenant.go
86
+    ovs-ofctl -O OpenFlow13 add-flow br0 "table=7, priority=0, arp, actions=flood"
87
+
88
+    ## linux bridge
89
+    ip link set lbr0 down || true
90
+    brctl delbr lbr0 || true
91
+    brctl addbr lbr0
92
+    ip addr add ${subnet_gateway}/${subnet_mask_len} dev lbr0
93
+    ip link set lbr0 up
94
+    brctl addif lbr0 vlinuxbr
95
+
96
+    # setup tun address
97
+    ip addr add ${tun_gateway}/${subnet_mask_len} dev ${TUN}
98
+    ip link set ${TUN} up
99
+    ip route add ${cluster_subnet} dev ${TUN} proto kernel scope link
100
+
101
+    ## iptables
102
+    iptables -t nat -D POSTROUTING -s ${cluster_subnet} ! -d ${cluster_subnet} -j MASQUERADE || true
103
+    iptables -t nat -A POSTROUTING -s ${cluster_subnet} ! -d ${cluster_subnet} -j MASQUERADE
104
+    iptables -D INPUT -p udp -m multiport --dports 4789 -m comment --comment "001 vxlan incoming" -j ACCEPT || true
105
+    iptables -D INPUT -i ${TUN} -m comment --comment "traffic from docker for internet" -j ACCEPT || true
106
+    lineno=$(iptables -nvL INPUT --line-numbers | grep "state RELATED,ESTABLISHED" | awk '{print $1}')
107
+    iptables -I INPUT $lineno -p udp -m multiport --dports 4789 -m comment --comment "001 vxlan incoming" -j ACCEPT
108
+    iptables -I INPUT $((lineno+1)) -i ${TUN} -m comment --comment "traffic from docker for internet" -j ACCEPT
109
+    fwd_lineno=$(iptables -nvL FORWARD --line-numbers | grep "reject-with icmp-host-prohibited" | tail -n 1 | awk '{print $1}')
110
+    iptables -I FORWARD $fwd_lineno -d ${cluster_subnet} -j ACCEPT
111
+    iptables -I FORWARD $fwd_lineno -s ${cluster_subnet} -j ACCEPT
112
+
113
+    ## docker
114
+    if [[ -z "${DOCKER_NETWORK_OPTIONS}" ]]
115
+    then
116
+        DOCKER_NETWORK_OPTIONS='-b=lbr0 --mtu=1450'
117
+    fi
118
+
119
+    mkdir -p /run/openshift-sdn
120
+    cat <<EOF > /run/openshift-sdn/docker-network
121
+# This file has been modified by openshift-sdn. Please modify the
122
+# DOCKER_NETWORK_OPTIONS variable in /etc/sysconfig/openshift-node if this
123
+# is an integrated install or /etc/sysconfig/openshift-sdn-node if this is a
124
+# standalone install.
125
+
126
+DOCKER_NETWORK_OPTIONS='${DOCKER_NETWORK_OPTIONS}'
127
+EOF
128
+
129
+    systemctl daemon-reload
130
+    systemctl restart docker.service
131
+
132
+    # disable iptables for lbr0
133
+    # for kernel version 3.18+, module br_netfilter needs to be loaded upfront
134
+    # for older ones, br_netfilter may not exist, but is covered by bridge (bridge-utils)
135
+    modprobe br_netfilter || true 
136
+    sysctl -w net.bridge.bridge-nf-call-iptables=0
137
+
138
+    # delete the subnet routing entry created because of lbr0
139
+    ip route del ${subnet} dev lbr0 proto kernel scope link src ${subnet_gateway} || true
140
+
141
+    mkdir -p /etc/openshift-sdn
142
+    echo "export OPENSHIFT_SDN_TAP1_ADDR=${tun_gateway}" >& "/etc/openshift-sdn/config.env"
143
+    echo "export OPENSHIFT_CLUSTER_SUBNET=${cluster_subnet}" >> "/etc/openshift-sdn/config.env"
144
+}
145
+
146
+set +e
147
+if ! setup_required; then
148
+    echo "SDN setup not required."
149
+    exit 140
150
+fi
151
+set -e
152
+
153
+lockwrap setup
0 154
new file mode 100644
... ...
@@ -0,0 +1,96 @@
0
+package multitenant
1
+
2
+import (
3
+	"encoding/hex"
4
+	"fmt"
5
+	log "github.com/golang/glog"
6
+	"net"
7
+	"os"
8
+	"os/exec"
9
+	"strconv"
10
+	"syscall"
11
+
12
+	"github.com/openshift/openshift-sdn/pkg/netutils"
13
+	netutils_server "github.com/openshift/openshift-sdn/pkg/netutils/server"
14
+)
15
+
16
+type FlowController struct {
17
+}
18
+
19
+func NewFlowController() *FlowController {
20
+	return &FlowController{}
21
+}
22
+
23
+func (c *FlowController) Setup(localSubnet, containerNetwork string) error {
24
+	_, ipnet, err := net.ParseCIDR(localSubnet)
25
+	subnetMaskLength, _ := ipnet.Mask.Size()
26
+	gateway := netutils.GenerateDefaultGateway(ipnet).String()
27
+	out, err := exec.Command("openshift-sdn-multitenant-setup.sh", gateway, ipnet.String(), containerNetwork, strconv.Itoa(subnetMaskLength), gateway).CombinedOutput()
28
+	log.Infof("Output of setup script:\n%s", out)
29
+	if err != nil {
30
+		exitErr, ok := err.(*exec.ExitError)
31
+		if ok {
32
+			status := exitErr.ProcessState.Sys().(syscall.WaitStatus)
33
+			if status.Exited() && status.ExitStatus() == 140 {
34
+				// valid, do nothing, its just a benevolent restart
35
+				return nil
36
+			}
37
+		}
38
+		log.Errorf("Error executing setup script. \n\tOutput: %s\n\tError: %v\n", out, err)
39
+	}
40
+	return err
41
+}
42
+
43
+func (c *FlowController) manageLocalIpam(ipnet *net.IPNet) error {
44
+	ipamHost := "127.0.0.1"
45
+	ipamPort := uint(9080)
46
+	inuse := make([]string, 0)
47
+	ipam, _ := netutils.NewIPAllocator(ipnet.String(), inuse)
48
+	f, err := os.Create("/etc/openshift-sdn/config.env")
49
+	if err != nil {
50
+		return err
51
+	}
52
+	_, err = f.WriteString(fmt.Sprintf("OPENSHIFT_SDN_TAP1_ADDR=%s\nOPENSHIFT_SDN_IPAM_SERVER=http://%s:%s", netutils.GenerateDefaultGateway(ipnet), ipamHost, ipamPort))
53
+	if err != nil {
54
+		return err
55
+	}
56
+	f.Close()
57
+	// listen and serve does not return the control
58
+	netutils_server.ListenAndServeNetutilServer(ipam, net.ParseIP(ipamHost), ipamPort, nil)
59
+	return nil
60
+}
61
+
62
+func (c *FlowController) AddOFRules(minionIP, subnet, localIP string) error {
63
+	if minionIP == localIP {
64
+		return nil
65
+	}
66
+
67
+	cookie := generateCookie(minionIP)
68
+	iprule := fmt.Sprintf("table=6,cookie=0x%s,priority=100,ip,nw_dst=%s,actions=move:NXM_NX_REG0[]->NXM_NX_TUN_ID[0..31],set_field:%s->tun_dst,output:1", cookie, subnet, minionIP)
69
+	arprule := fmt.Sprintf("table=7,cookie=0x%s,priority=100,arp,nw_dst=%s,actions=move:NXM_NX_REG0[]->NXM_NX_TUN_ID[0..31],set_field:%s->tun_dst,output:1", cookie, subnet, minionIP)
70
+	o, e := exec.Command("ovs-ofctl", "-O", "OpenFlow13", "add-flow", "br0", iprule).CombinedOutput()
71
+	log.Infof("Output of adding %s: %s (%v)", iprule, o, e)
72
+	o, e = exec.Command("ovs-ofctl", "-O", "OpenFlow13", "add-flow", "br0", arprule).CombinedOutput()
73
+	log.Infof("Output of adding %s: %s (%v)", arprule, o, e)
74
+	return e
75
+}
76
+
77
+func (c *FlowController) DelOFRules(minion, localIP string) error {
78
+	if minion == localIP {
79
+		return nil
80
+	}
81
+
82
+	log.Infof("Calling del rules for %s", minion)
83
+	cookie := generateCookie(minion)
84
+	iprule := fmt.Sprintf("table=6,cookie=0x%s/0xffffffff", cookie)
85
+	arprule := fmt.Sprintf("table=7,cookie=0x%s/0xffffffff", cookie)
86
+	o, e := exec.Command("ovs-ofctl", "-O", "OpenFlow13", "del-flows", "br0", iprule).CombinedOutput()
87
+	log.Infof("Output of deleting local ip rules %s (%v)", o, e)
88
+	o, e = exec.Command("ovs-ofctl", "-O", "OpenFlow13", "del-flows", "br0", arprule).CombinedOutput()
89
+	log.Infof("Output of deleting local arp rules %s (%v)", o, e)
90
+	return e
91
+}
92
+
93
+func generateCookie(ip string) string {
94
+	return hex.EncodeToString(net.ParseIP(ip).To4())
95
+}
0 96
new file mode 100644
... ...
@@ -0,0 +1,385 @@
0
+package registry
1
+
2
+import (
3
+	"encoding/json"
4
+	"errors"
5
+	"fmt"
6
+	"path"
7
+	"strconv"
8
+	"sync"
9
+	"time"
10
+
11
+	"github.com/coreos/go-etcd/etcd"
12
+	log "github.com/golang/glog"
13
+	"github.com/openshift/openshift-sdn/ovssubnet/api"
14
+)
15
+
16
+type EtcdConfig struct {
17
+	Endpoints        []string
18
+	Keyfile          string
19
+	Certfile         string
20
+	CAFile           string
21
+	SubnetPath       string
22
+	SubnetConfigPath string
23
+	MinionPath       string
24
+}
25
+
26
+type EtcdSubnetRegistry struct {
27
+	mux     sync.Mutex
28
+	cli     *etcd.Client
29
+	etcdCfg *EtcdConfig
30
+}
31
+
32
+func newMinionEvent(action, key, value string) *api.MinionEvent {
33
+	min := &api.MinionEvent{}
34
+	switch action {
35
+	case "delete", "deleted", "expired":
36
+		min.Type = api.Deleted
37
+	default:
38
+		min.Type = api.Added
39
+	}
40
+
41
+	if key != "" {
42
+		_, min.Minion = path.Split(key)
43
+		return min
44
+	}
45
+
46
+	fmt.Printf("Error decoding minion event: nil key (%s,%s,%s).\n", action, key, value)
47
+	return nil
48
+}
49
+
50
+func newSubnetEvent(resp *etcd.Response) *api.SubnetEvent {
51
+	var value string
52
+	_, minkey := path.Split(resp.Node.Key)
53
+	var t api.EventType
54
+	switch resp.Action {
55
+	case "deleted", "delete", "expired":
56
+		t = api.Deleted
57
+		value = resp.PrevNode.Value
58
+	default:
59
+		t = api.Added
60
+		value = resp.Node.Value
61
+	}
62
+	var sub api.Subnet
63
+	if err := json.Unmarshal([]byte(value), &sub); err == nil {
64
+		return &api.SubnetEvent{
65
+			Type:   t,
66
+			Minion: minkey,
67
+			Sub:    sub,
68
+		}
69
+	}
70
+	log.Errorf("Failed to unmarshal response: %v", resp)
71
+	return nil
72
+}
73
+
74
+func newEtcdClient(c *EtcdConfig) (*etcd.Client, error) {
75
+	if c.Keyfile != "" || c.Certfile != "" || c.CAFile != "" {
76
+		return etcd.NewTLSClient(c.Endpoints, c.Certfile, c.Keyfile, c.CAFile)
77
+	} else {
78
+		return etcd.NewClient(c.Endpoints), nil
79
+	}
80
+}
81
+
82
+func (sub *EtcdSubnetRegistry) CheckEtcdIsAlive(seconds uint64) bool {
83
+	for {
84
+		status := sub.client().SyncCluster()
85
+		log.Infof("Etcd cluster status: %v", status)
86
+		if status {
87
+			return status
88
+		}
89
+		if seconds <= 0 {
90
+			break
91
+		}
92
+		time.Sleep(5 * time.Second)
93
+		seconds -= 5
94
+	}
95
+	return false
96
+}
97
+
98
+func NewEtcdSubnetRegistry(config *EtcdConfig) (api.SubnetRegistry, error) {
99
+	r := &EtcdSubnetRegistry{
100
+		etcdCfg: config,
101
+	}
102
+
103
+	var err error
104
+	r.cli, err = newEtcdClient(config)
105
+	if err != nil {
106
+		return nil, err
107
+	}
108
+
109
+	return r, nil
110
+}
111
+
112
+func (sub *EtcdSubnetRegistry) InitSubnets() error {
113
+	key := sub.etcdCfg.SubnetPath
114
+	_, err := sub.client().SetDir(key, 0)
115
+	if err != nil {
116
+		return err
117
+	}
118
+	key = sub.etcdCfg.SubnetConfigPath
119
+	_, err = sub.client().SetDir(key, 0)
120
+	return err
121
+}
122
+
123
+func (sub *EtcdSubnetRegistry) InitMinions() error {
124
+	key := sub.etcdCfg.MinionPath
125
+	_, err := sub.client().SetDir(key, 0)
126
+	return err
127
+}
128
+
129
+func (sub *EtcdSubnetRegistry) GetMinions() (*[]string, error) {
130
+	key := sub.etcdCfg.MinionPath
131
+	resp, err := sub.client().Get(key, false, true)
132
+	if err != nil {
133
+		return nil, err
134
+	}
135
+
136
+	if resp.Node.Dir == false {
137
+		return nil, errors.New("Minion path is not a directory")
138
+	}
139
+
140
+	minions := make([]string, 0)
141
+
142
+	for _, node := range resp.Node.Nodes {
143
+		if node.Key == "" {
144
+			log.Errorf("Error unmarshalling GetMinions response node %s", node.Key)
145
+			continue
146
+		}
147
+		_, minion := path.Split(node.Key)
148
+		minions = append(minions, minion)
149
+	}
150
+	return &minions, nil
151
+}
152
+
153
+func (sub *EtcdSubnetRegistry) GetSubnets() (*[]api.Subnet, error) {
154
+	key := sub.etcdCfg.SubnetPath
155
+	resp, err := sub.client().Get(key, false, true)
156
+	if err != nil {
157
+		return nil, err
158
+	}
159
+
160
+	if resp.Node.Dir == false {
161
+		return nil, errors.New("Subnet path is not a directory")
162
+	}
163
+
164
+	subnets := make([]api.Subnet, 0)
165
+
166
+	for _, node := range resp.Node.Nodes {
167
+		var s api.Subnet
168
+		err := json.Unmarshal([]byte(node.Value), &s)
169
+		if err != nil {
170
+			log.Errorf("Error unmarshalling GetSubnets response for node %s: %s", node.Value, err.Error())
171
+			continue
172
+		}
173
+		subnets = append(subnets, s)
174
+	}
175
+	return &subnets, err
176
+}
177
+
178
+func (sub *EtcdSubnetRegistry) GetSubnet(minionip string) (*api.Subnet, error) {
179
+	key := path.Join(sub.etcdCfg.SubnetPath, minionip)
180
+	resp, err := sub.client().Get(key, false, false)
181
+	if err == nil {
182
+		log.Infof("Unmarshalling response: %s", resp.Node.Value)
183
+		var sub api.Subnet
184
+		if err = json.Unmarshal([]byte(resp.Node.Value), &sub); err == nil {
185
+			return &sub, nil
186
+		}
187
+		return nil, err
188
+	}
189
+	return nil, err
190
+}
191
+
192
+func (sub *EtcdSubnetRegistry) DeleteSubnet(minion string) error {
193
+	key := path.Join(sub.etcdCfg.SubnetPath, minion)
194
+	_, err := sub.client().Delete(key, false)
195
+	return err
196
+}
197
+
198
+func (sub *EtcdSubnetRegistry) WriteNetworkConfig(network string, subnetLength uint) error {
199
+	key := path.Join(sub.etcdCfg.SubnetConfigPath, "ContainerNetwork")
200
+	_, err := sub.client().Create(key, network, 0)
201
+	if err != nil {
202
+		log.Warningf("Found existing network configuration, overwriting it.")
203
+		_, err = sub.client().Update(key, network, 0)
204
+		if err != nil {
205
+			log.Errorf("Failed to write Network configuration to etcd: %v", err)
206
+			return err
207
+		}
208
+	}
209
+
210
+	key = path.Join(sub.etcdCfg.SubnetConfigPath, "SubnetLength")
211
+	data := strconv.FormatUint(uint64(subnetLength), 10)
212
+	_, err = sub.client().Create(key, data, 0)
213
+	if err != nil {
214
+		_, err = sub.client().Update(key, data, 0)
215
+		if err != nil {
216
+			log.Errorf("Failed to write Network configuration to etcd: %v", err)
217
+			return err
218
+		}
219
+	}
220
+	return nil
221
+}
222
+
223
+func (sub *EtcdSubnetRegistry) GetContainerNetwork() (string, error) {
224
+	key := path.Join(sub.etcdCfg.SubnetConfigPath, "ContainerNetwork")
225
+	resp, err := sub.client().Get(key, false, false)
226
+	if err != nil {
227
+		return "", err
228
+	}
229
+	return resp.Node.Value, err
230
+}
231
+
232
+func (sub *EtcdSubnetRegistry) GetSubnetLength() (uint64, error) {
233
+	key := path.Join(sub.etcdCfg.SubnetConfigPath, "SubnetLength")
234
+	resp, err := sub.client().Get(key, false, false)
235
+	if err == nil {
236
+		return strconv.ParseUint(resp.Node.Value, 10, 0)
237
+	}
238
+	return 0, err
239
+}
240
+
241
+func (sub *EtcdSubnetRegistry) CreateMinion(minion string, data string) error {
242
+	key := path.Join(sub.etcdCfg.MinionPath, minion)
243
+	_, err := sub.client().Get(key, false, false)
244
+	if err != nil {
245
+		// good, it does not exist, write it
246
+		_, err = sub.client().Create(key, data, 0)
247
+		if err != nil {
248
+			log.Errorf("Failed to write new subnet to etcd: %v", err)
249
+			return err
250
+		}
251
+	}
252
+
253
+	return nil
254
+}
255
+
256
+func (sub *EtcdSubnetRegistry) CreateSubnet(minion string, subnet *api.Subnet) error {
257
+	subbytes, _ := json.Marshal(subnet)
258
+	data := string(subbytes)
259
+	log.Infof("Minion subnet structure: %s", data)
260
+	key := path.Join(sub.etcdCfg.SubnetPath, minion)
261
+	_, err := sub.client().Create(key, data, 0)
262
+	if err != nil {
263
+		_, err = sub.client().Update(key, data, 0)
264
+		if err != nil {
265
+			log.Errorf("Failed to write new subnet to etcd: %v", err)
266
+			return err
267
+		}
268
+	}
269
+
270
+	return nil
271
+}
272
+
273
+func (sub *EtcdSubnetRegistry) WatchMinions(receiver chan *api.MinionEvent, stop chan bool) error {
274
+	var rev uint64
275
+	rev = 0
276
+	key := sub.etcdCfg.MinionPath
277
+	log.Infof("Watching %s for new minions.", key)
278
+	for {
279
+		resp, err := sub.watch(key, rev, stop)
280
+		if err != nil && err == etcd.ErrWatchStoppedByUser {
281
+			log.Infof("New subnet event error: %v", err)
282
+			return err
283
+		}
284
+		if resp == nil || err != nil {
285
+			continue
286
+		}
287
+		rev = resp.Node.ModifiedIndex + 1
288
+		log.Infof("Issuing a minion event: %v", resp)
289
+		minevent := newMinionEvent(resp.Action, resp.Node.Key, resp.Node.Value)
290
+		receiver <- minevent
291
+	}
292
+}
293
+
294
+func (sub *EtcdSubnetRegistry) watch(key string, rev uint64, stop chan bool) (*etcd.Response, error) {
295
+	rawResp, err := sub.client().RawWatch(key, rev, true, nil, stop)
296
+
297
+	if err != nil {
298
+		if err == etcd.ErrWatchStoppedByUser {
299
+			return nil, err
300
+		} else {
301
+			log.Warningf("Temporary error while watching %s: %v\n", key, err)
302
+			time.Sleep(time.Second)
303
+			sub.resetClient()
304
+			return nil, nil
305
+		}
306
+	}
307
+
308
+	if len(rawResp.Body) == 0 {
309
+		// etcd timed out, go back but recreate the client as the underlying
310
+		// http transport gets hosed (http://code.google.com/p/go/issues/detail?id=8648)
311
+		sub.resetClient()
312
+		return nil, nil
313
+	}
314
+
315
+	return rawResp.Unmarshal()
316
+}
317
+
318
+func (sub *EtcdSubnetRegistry) WatchSubnets(receiver chan *api.SubnetEvent, stop chan bool) error {
319
+	for {
320
+		var rev uint64
321
+		rev = 0
322
+		key := sub.etcdCfg.SubnetPath
323
+		resp, err := sub.watch(key, rev, stop)
324
+		if resp == nil && err == nil {
325
+			continue
326
+		}
327
+		rev = resp.Node.ModifiedIndex + 1
328
+		if err != nil && err == etcd.ErrWatchStoppedByUser {
329
+			log.Infof("New subnet event error: %v", err)
330
+			return err
331
+		}
332
+		subevent := newSubnetEvent(resp)
333
+		log.Infof("New subnet event: %v, %v", subevent, resp)
334
+		receiver <- subevent
335
+	}
336
+}
337
+
338
+func (sub *EtcdSubnetRegistry) WatchNamespaces(receiver chan *api.NamespaceEvent, stop chan bool) error {
339
+	// TODO
340
+	return nil
341
+}
342
+
343
+func (sub *EtcdSubnetRegistry) WatchNetNamespaces(receiver chan *api.NetNamespaceEvent, stop chan bool) error {
344
+	// TODO
345
+	return nil
346
+}
347
+
348
+func (sub *EtcdSubnetRegistry) GetNetNamespaces() ([]api.NetNamespace, error) {
349
+	nslist := make([]api.NetNamespace, 0)
350
+	// TODO
351
+	return nslist, nil
352
+}
353
+
354
+func (sub *EtcdSubnetRegistry) GetNetNamespace(name string) (api.NetNamespace, error) {
355
+	// TODO
356
+	return api.NetNamespace{}, nil
357
+}
358
+
359
+func (sub *EtcdSubnetRegistry) WriteNetNamespace(name string, id uint) error {
360
+	// TODO
361
+	return nil
362
+}
363
+
364
+func (sub *EtcdSubnetRegistry) DeleteNetNamespace(name string) error {
365
+	// TODO
366
+	return nil
367
+}
368
+
369
+func (sub *EtcdSubnetRegistry) client() *etcd.Client {
370
+	sub.mux.Lock()
371
+	defer sub.mux.Unlock()
372
+	return sub.cli
373
+}
374
+
375
+func (sub *EtcdSubnetRegistry) resetClient() {
376
+	sub.mux.Lock()
377
+	defer sub.mux.Unlock()
378
+
379
+	var err error
380
+	sub.cli, err = newEtcdClient(sub.etcdCfg)
381
+	if err != nil {
382
+		panic(fmt.Errorf("resetClient: error recreating etcd client: %v", err))
383
+	}
384
+}
0 385
deleted file mode 100644
... ...
@@ -1,43 +0,0 @@
1
-package api
2
-
3
-type EventType string
4
-
5
-const (
6
-	Added   EventType = "ADDED"
7
-	Deleted EventType = "DELETED"
8
-)
9
-
10
-type SubnetRegistry interface {
11
-	InitSubnets() error
12
-	GetSubnets() (*[]Subnet, error)
13
-	GetSubnet(minion string) (*Subnet, error)
14
-	DeleteSubnet(minion string) error
15
-	CreateSubnet(sn string, sub *Subnet) error
16
-	WatchSubnets(receiver chan *SubnetEvent, stop chan bool) error
17
-
18
-	InitMinions() error
19
-	GetMinions() (*[]string, error)
20
-	CreateMinion(minion string, data string) error
21
-	WatchMinions(receiver chan *MinionEvent, stop chan bool) error
22
-
23
-	WriteNetworkConfig(network string, subnetLength uint) error
24
-	GetContainerNetwork() (string, error)
25
-	GetSubnetLength() (uint64, error)
26
-	CheckEtcdIsAlive(seconds uint64) bool
27
-}
28
-
29
-type SubnetEvent struct {
30
-	Type   EventType
31
-	Minion string
32
-	Sub    Subnet
33
-}
34
-
35
-type MinionEvent struct {
36
-	Type   EventType
37
-	Minion string
38
-}
39
-
40
-type Subnet struct {
41
-	Minion string
42
-	Sub    string
43
-}
44 1
new file mode 100644
... ...
@@ -0,0 +1,56 @@
0
+package netutils
1
+
2
+import (
3
+	"fmt"
4
+)
5
+
6
+type NetIDAllocator struct {
7
+	min      uint
8
+	max      uint
9
+	allocMap map[uint]bool
10
+}
11
+
12
+func NewNetIDAllocator(min uint, max uint, inUse []uint) (*NetIDAllocator, error) {
13
+	if max <= min {
14
+		return nil, fmt.Errorf("Min should be lesser than max value (Min: %d, Max: %d)", min, max)
15
+	}
16
+
17
+	amap := make(map[uint]bool)
18
+	for _, netid := range inUse {
19
+		if netid < min || netid > max {
20
+			fmt.Println("Provided net id doesn't belong to range: ", min, max)
21
+			continue
22
+		}
23
+		amap[netid] = true
24
+	}
25
+
26
+	return &NetIDAllocator{min: min, max: max, allocMap: amap}, nil
27
+}
28
+
29
+func (nia *NetIDAllocator) GetNetID() (uint, error) {
30
+	var i uint
31
+	// We exclude the last address as it is reserved for broadcast
32
+	for i = nia.min; i <= nia.max; i++ {
33
+		taken, found := nia.allocMap[i]
34
+		if !found || !taken {
35
+			nia.allocMap[i] = true
36
+			return i, nil
37
+		}
38
+	}
39
+
40
+	return 0, fmt.Errorf("No NetIDs available.")
41
+}
42
+
43
+func (nia *NetIDAllocator) ReleaseNetID(netid uint) error {
44
+	if nia.min > netid || nia.max < netid {
45
+		return fmt.Errorf("Provided net id %v doesn't belong to the given range (%v-%v)", netid, nia.min, nia.max)
46
+	}
47
+
48
+	taken, found := nia.allocMap[netid]
49
+	if !found || !taken {
50
+		return fmt.Errorf("Provided net id %v is already available.", netid)
51
+	}
52
+
53
+	nia.allocMap[netid] = false
54
+	return nil
55
+}
... ...
@@ -71,7 +71,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
71 71
     "num_minions"       => ENV['OPENSHIFT_NUM_MINIONS'] || 2,
72 72
     "rebuild_yum_cache" => false,
73 73
     "cpus"              => ENV['OPENSHIFT_NUM_CPUS'] || 2,
74
-    "memory"            => ENV['OPENSHIFT_MEMORY'] || 1024,
74
+    "memory"            => ENV['OPENSHIFT_MEMORY'] || 2048,
75 75
     "sync_folders_type" => nil,
76 76
     "virtualbox"        => {
77 77
       "box_name" => "fedora_inst",
... ...
@@ -6805,6 +6805,432 @@
6805 6805
     ]
6806 6806
    },
6807 6807
    {
6808
+    "path": "/oapi/v1/netnamespaces",
6809
+    "description": "OpenShift REST API, version v1",
6810
+    "operations": [
6811
+     {
6812
+      "type": "v1.NetNamespaceList",
6813
+      "method": "GET",
6814
+      "summary": "list or watch objects of kind NetNamespace",
6815
+      "nickname": "listNetNamespace",
6816
+      "parameters": [
6817
+       {
6818
+        "type": "string",
6819
+        "paramType": "query",
6820
+        "name": "pretty",
6821
+        "description": "If 'true', then the output is pretty printed.",
6822
+        "required": false,
6823
+        "allowMultiple": false
6824
+       },
6825
+       {
6826
+        "type": "string",
6827
+        "paramType": "query",
6828
+        "name": "labelSelector",
6829
+        "description": "a selector to restrict the list of returned objects by their labels; defaults to everything",
6830
+        "required": false,
6831
+        "allowMultiple": false
6832
+       },
6833
+       {
6834
+        "type": "string",
6835
+        "paramType": "query",
6836
+        "name": "fieldSelector",
6837
+        "description": "a selector to restrict the list of returned objects by their fields; defaults to everything",
6838
+        "required": false,
6839
+        "allowMultiple": false
6840
+       },
6841
+       {
6842
+        "type": "boolean",
6843
+        "paramType": "query",
6844
+        "name": "watch",
6845
+        "description": "watch for changes to the described resources and return them as a stream of add, update, and remove notifications; specify resourceVersion",
6846
+        "required": false,
6847
+        "allowMultiple": false
6848
+       },
6849
+       {
6850
+        "type": "string",
6851
+        "paramType": "query",
6852
+        "name": "resourceVersion",
6853
+        "description": "when specified with a watch call, shows changes that occur after that particular version of a resource; defaults to changes from the beginning of history",
6854
+        "required": false,
6855
+        "allowMultiple": false
6856
+       }
6857
+      ],
6858
+      "responseMessages": [
6859
+       {
6860
+        "code": 200,
6861
+        "message": "OK",
6862
+        "responseModel": "v1.NetNamespaceList"
6863
+       }
6864
+      ],
6865
+      "produces": [
6866
+       "application/json"
6867
+      ],
6868
+      "consumes": [
6869
+       "*/*"
6870
+      ]
6871
+     },
6872
+     {
6873
+      "type": "v1.NetNamespace",
6874
+      "method": "POST",
6875
+      "summary": "create a NetNamespace",
6876
+      "nickname": "createNetNamespace",
6877
+      "parameters": [
6878
+       {
6879
+        "type": "string",
6880
+        "paramType": "query",
6881
+        "name": "pretty",
6882
+        "description": "If 'true', then the output is pretty printed.",
6883
+        "required": false,
6884
+        "allowMultiple": false
6885
+       },
6886
+       {
6887
+        "type": "v1.NetNamespace",
6888
+        "paramType": "body",
6889
+        "name": "body",
6890
+        "description": "",
6891
+        "required": true,
6892
+        "allowMultiple": false
6893
+       }
6894
+      ],
6895
+      "responseMessages": [
6896
+       {
6897
+        "code": 200,
6898
+        "message": "OK",
6899
+        "responseModel": "v1.NetNamespace"
6900
+       }
6901
+      ],
6902
+      "produces": [
6903
+       "application/json"
6904
+      ],
6905
+      "consumes": [
6906
+       "*/*"
6907
+      ]
6908
+     }
6909
+    ]
6910
+   },
6911
+   {
6912
+    "path": "/oapi/v1/watch/netnamespaces",
6913
+    "description": "OpenShift REST API, version v1",
6914
+    "operations": [
6915
+     {
6916
+      "type": "json.WatchEvent",
6917
+      "method": "GET",
6918
+      "summary": "watch individual changes to a list of NetNamespace",
6919
+      "nickname": "watchNetNamespaceList",
6920
+      "parameters": [
6921
+       {
6922
+        "type": "string",
6923
+        "paramType": "query",
6924
+        "name": "pretty",
6925
+        "description": "If 'true', then the output is pretty printed.",
6926
+        "required": false,
6927
+        "allowMultiple": false
6928
+       },
6929
+       {
6930
+        "type": "string",
6931
+        "paramType": "query",
6932
+        "name": "labelSelector",
6933
+        "description": "a selector to restrict the list of returned objects by their labels; defaults to everything",
6934
+        "required": false,
6935
+        "allowMultiple": false
6936
+       },
6937
+       {
6938
+        "type": "string",
6939
+        "paramType": "query",
6940
+        "name": "fieldSelector",
6941
+        "description": "a selector to restrict the list of returned objects by their fields; defaults to everything",
6942
+        "required": false,
6943
+        "allowMultiple": false
6944
+       },
6945
+       {
6946
+        "type": "boolean",
6947
+        "paramType": "query",
6948
+        "name": "watch",
6949
+        "description": "watch for changes to the described resources and return them as a stream of add, update, and remove notifications; specify resourceVersion",
6950
+        "required": false,
6951
+        "allowMultiple": false
6952
+       },
6953
+       {
6954
+        "type": "string",
6955
+        "paramType": "query",
6956
+        "name": "resourceVersion",
6957
+        "description": "when specified with a watch call, shows changes that occur after that particular version of a resource; defaults to changes from the beginning of history",
6958
+        "required": false,
6959
+        "allowMultiple": false
6960
+       }
6961
+      ],
6962
+      "responseMessages": [
6963
+       {
6964
+        "code": 200,
6965
+        "message": "OK",
6966
+        "responseModel": "json.WatchEvent"
6967
+       }
6968
+      ],
6969
+      "produces": [
6970
+       "application/json"
6971
+      ],
6972
+      "consumes": [
6973
+       "*/*"
6974
+      ]
6975
+     }
6976
+    ]
6977
+   },
6978
+   {
6979
+    "path": "/oapi/v1/netnamespaces/{name}",
6980
+    "description": "OpenShift REST API, version v1",
6981
+    "operations": [
6982
+     {
6983
+      "type": "v1.NetNamespace",
6984
+      "method": "GET",
6985
+      "summary": "read the specified NetNamespace",
6986
+      "nickname": "readNetNamespace",
6987
+      "parameters": [
6988
+       {
6989
+        "type": "string",
6990
+        "paramType": "query",
6991
+        "name": "pretty",
6992
+        "description": "If 'true', then the output is pretty printed.",
6993
+        "required": false,
6994
+        "allowMultiple": false
6995
+       },
6996
+       {
6997
+        "type": "string",
6998
+        "paramType": "path",
6999
+        "name": "name",
7000
+        "description": "name of the NetNamespace",
7001
+        "required": true,
7002
+        "allowMultiple": false
7003
+       }
7004
+      ],
7005
+      "responseMessages": [
7006
+       {
7007
+        "code": 200,
7008
+        "message": "OK",
7009
+        "responseModel": "v1.NetNamespace"
7010
+       }
7011
+      ],
7012
+      "produces": [
7013
+       "application/json"
7014
+      ],
7015
+      "consumes": [
7016
+       "*/*"
7017
+      ]
7018
+     },
7019
+     {
7020
+      "type": "v1.NetNamespace",
7021
+      "method": "PUT",
7022
+      "summary": "replace the specified NetNamespace",
7023
+      "nickname": "replaceNetNamespace",
7024
+      "parameters": [
7025
+       {
7026
+        "type": "string",
7027
+        "paramType": "query",
7028
+        "name": "pretty",
7029
+        "description": "If 'true', then the output is pretty printed.",
7030
+        "required": false,
7031
+        "allowMultiple": false
7032
+       },
7033
+       {
7034
+        "type": "v1.NetNamespace",
7035
+        "paramType": "body",
7036
+        "name": "body",
7037
+        "description": "",
7038
+        "required": true,
7039
+        "allowMultiple": false
7040
+       },
7041
+       {
7042
+        "type": "string",
7043
+        "paramType": "path",
7044
+        "name": "name",
7045
+        "description": "name of the NetNamespace",
7046
+        "required": true,
7047
+        "allowMultiple": false
7048
+       }
7049
+      ],
7050
+      "responseMessages": [
7051
+       {
7052
+        "code": 200,
7053
+        "message": "OK",
7054
+        "responseModel": "v1.NetNamespace"
7055
+       }
7056
+      ],
7057
+      "produces": [
7058
+       "application/json"
7059
+      ],
7060
+      "consumes": [
7061
+       "*/*"
7062
+      ]
7063
+     },
7064
+     {
7065
+      "type": "v1.NetNamespace",
7066
+      "method": "PATCH",
7067
+      "summary": "partially update the specified NetNamespace",
7068
+      "nickname": "patchNetNamespace",
7069
+      "parameters": [
7070
+       {
7071
+        "type": "string",
7072
+        "paramType": "query",
7073
+        "name": "pretty",
7074
+        "description": "If 'true', then the output is pretty printed.",
7075
+        "required": false,
7076
+        "allowMultiple": false
7077
+       },
7078
+       {
7079
+        "type": "api.Patch",
7080
+        "paramType": "body",
7081
+        "name": "body",
7082
+        "description": "",
7083
+        "required": true,
7084
+        "allowMultiple": false
7085
+       },
7086
+       {
7087
+        "type": "string",
7088
+        "paramType": "path",
7089
+        "name": "name",
7090
+        "description": "name of the NetNamespace",
7091
+        "required": true,
7092
+        "allowMultiple": false
7093
+       }
7094
+      ],
7095
+      "responseMessages": [
7096
+       {
7097
+        "code": 200,
7098
+        "message": "OK",
7099
+        "responseModel": "v1.NetNamespace"
7100
+       }
7101
+      ],
7102
+      "produces": [
7103
+       "application/json"
7104
+      ],
7105
+      "consumes": [
7106
+       "application/json-patch+json",
7107
+       "application/merge-patch+json",
7108
+       "application/strategic-merge-patch+json"
7109
+      ]
7110
+     },
7111
+     {
7112
+      "type": "v1.Status",
7113
+      "method": "DELETE",
7114
+      "summary": "delete a NetNamespace",
7115
+      "nickname": "deleteNetNamespace",
7116
+      "parameters": [
7117
+       {
7118
+        "type": "string",
7119
+        "paramType": "query",
7120
+        "name": "pretty",
7121
+        "description": "If 'true', then the output is pretty printed.",
7122
+        "required": false,
7123
+        "allowMultiple": false
7124
+       },
7125
+       {
7126
+        "type": "v1.DeleteOptions",
7127
+        "paramType": "body",
7128
+        "name": "body",
7129
+        "description": "",
7130
+        "required": true,
7131
+        "allowMultiple": false
7132
+       },
7133
+       {
7134
+        "type": "string",
7135
+        "paramType": "path",
7136
+        "name": "name",
7137
+        "description": "name of the NetNamespace",
7138
+        "required": true,
7139
+        "allowMultiple": false
7140
+       }
7141
+      ],
7142
+      "responseMessages": [
7143
+       {
7144
+        "code": 200,
7145
+        "message": "OK",
7146
+        "responseModel": "v1.Status"
7147
+       }
7148
+      ],
7149
+      "produces": [
7150
+       "application/json"
7151
+      ],
7152
+      "consumes": [
7153
+       "*/*"
7154
+      ]
7155
+     }
7156
+    ]
7157
+   },
7158
+   {
7159
+    "path": "/oapi/v1/watch/netnamespaces/{name}",
7160
+    "description": "OpenShift REST API, version v1",
7161
+    "operations": [
7162
+     {
7163
+      "type": "json.WatchEvent",
7164
+      "method": "GET",
7165
+      "summary": "watch changes to an object of kind NetNamespace",
7166
+      "nickname": "watchNetNamespace",
7167
+      "parameters": [
7168
+       {
7169
+        "type": "string",
7170
+        "paramType": "query",
7171
+        "name": "pretty",
7172
+        "description": "If 'true', then the output is pretty printed.",
7173
+        "required": false,
7174
+        "allowMultiple": false
7175
+       },
7176
+       {
7177
+        "type": "string",
7178
+        "paramType": "query",
7179
+        "name": "labelSelector",
7180
+        "description": "a selector to restrict the list of returned objects by their labels; defaults to everything",
7181
+        "required": false,
7182
+        "allowMultiple": false
7183
+       },
7184
+       {
7185
+        "type": "string",
7186
+        "paramType": "query",
7187
+        "name": "fieldSelector",
7188
+        "description": "a selector to restrict the list of returned objects by their fields; defaults to everything",
7189
+        "required": false,
7190
+        "allowMultiple": false
7191
+       },
7192
+       {
7193
+        "type": "boolean",
7194
+        "paramType": "query",
7195
+        "name": "watch",
7196
+        "description": "watch for changes to the described resources and return them as a stream of add, update, and remove notifications; specify resourceVersion",
7197
+        "required": false,
7198
+        "allowMultiple": false
7199
+       },
7200
+       {
7201
+        "type": "string",
7202
+        "paramType": "query",
7203
+        "name": "resourceVersion",
7204
+        "description": "when specified with a watch call, shows changes that occur after that particular version of a resource; defaults to changes from the beginning of history",
7205
+        "required": false,
7206
+        "allowMultiple": false
7207
+       },
7208
+       {
7209
+        "type": "string",
7210
+        "paramType": "path",
7211
+        "name": "name",
7212
+        "description": "name of the NetNamespace",
7213
+        "required": true,
7214
+        "allowMultiple": false
7215
+       }
7216
+      ],
7217
+      "responseMessages": [
7218
+       {
7219
+        "code": 200,
7220
+        "message": "OK",
7221
+        "responseModel": "json.WatchEvent"
7222
+       }
7223
+      ],
7224
+      "produces": [
7225
+       "application/json"
7226
+      ],
7227
+      "consumes": [
7228
+       "*/*"
7229
+      ]
7230
+     }
7231
+    ]
7232
+   },
7233
+   {
6808 7234
     "path": "/oapi/v1/oauthaccesstokens",
6809 7235
     "description": "OpenShift REST API, version v1",
6810 7236
     "operations": [
... ...
@@ -15639,6 +16065,60 @@
15639 15639
      }
15640 15640
     }
15641 15641
    },
15642
+   "v1.NetNamespaceList": {
15643
+    "id": "v1.NetNamespaceList",
15644
+    "required": [
15645
+     "items"
15646
+    ],
15647
+    "properties": {
15648
+     "kind": {
15649
+      "type": "string",
15650
+      "description": "kind of object, in CamelCase; cannot be updated; see http://releases.k8s.io/v1.0.0/docs/api-conventions.md#types-kinds"
15651
+     },
15652
+     "apiVersion": {
15653
+      "type": "string",
15654
+      "description": "version of the schema the object should have; see http://releases.k8s.io/v1.0.0/docs/api-conventions.md#resources"
15655
+     },
15656
+     "metadata": {
15657
+      "$ref": "v1.ListMeta"
15658
+     },
15659
+     "items": {
15660
+      "type": "array",
15661
+      "items": {
15662
+       "$ref": "v1.NetNamespace"
15663
+      },
15664
+      "description": "list of net namespaces"
15665
+     }
15666
+    }
15667
+   },
15668
+   "v1.NetNamespace": {
15669
+    "id": "v1.NetNamespace",
15670
+    "required": [
15671
+     "netname",
15672
+     "netid"
15673
+    ],
15674
+    "properties": {
15675
+     "kind": {
15676
+      "type": "string",
15677
+      "description": "kind of object, in CamelCase; cannot be updated; see http://releases.k8s.io/v1.0.0/docs/api-conventions.md#types-kinds"
15678
+     },
15679
+     "apiVersion": {
15680
+      "type": "string",
15681
+      "description": "version of the schema the object should have; see http://releases.k8s.io/v1.0.0/docs/api-conventions.md#resources"
15682
+     },
15683
+     "metadata": {
15684
+      "$ref": "v1.ObjectMeta"
15685
+     },
15686
+     "netname": {
15687
+      "type": "string",
15688
+      "description": "Name of the network namespace."
15689
+     },
15690
+     "netid": {
15691
+      "type": "uint",
15692
+      "description": "NetID of the network namespace assigned to each overlay network packet."
15693
+     }
15694
+    }
15695
+   },
15642 15696
    "v1.OAuthAccessTokenList": {
15643 15697
     "id": "v1.OAuthAccessTokenList",
15644 15698
     "required": [
... ...
@@ -289,15 +289,18 @@ install -p -m 0644 rel-eng/docker-sdn-ovs.conf %{buildroot}%{_unitdir}/docker.se
289 289
 for pkgname in openshift atomic-enterprise
290 290
 do
291 291
 
292
-  pushd _thirdpartyhacks/src/%{sdn_import_path}/ovssubnet/bin
292
+  pushd _thirdpartyhacks/src/%{sdn_import_path}/ovssubnet/controller/kube/bin
293 293
      install -p -m 755 %{name}-ovs-subnet %{buildroot}%{kube_plugin_path}/${pkgname}-ovs-subnet
294 294
      install -p -m 755 %{name}-sdn-kube-subnet-setup.sh %{buildroot}%{_bindir}/${pkgname}-sdn-kube-subnet-setup.sh
295 295
   popd
296
+  pushd _thirdpartyhacks/src/%{sdn_import_path}/ovssubnet/controller/multitenant/bin
297
+     install -p -m 755 %{name}-ovs-multitenant %{buildroot}%{_bindir}/${pkgname}-ovs-multitenant
298
+     install -p -m 755 %{name}-sdn-multitenant-setup.sh %{buildroot}%{_bindir}/${pkgname}-sdn-multitenant-setup.sh
299
+  popd
296 300
   install -d -m 0755 %{buildroot}%{_unitdir}/${pkgname}-node.service.d
297 301
   install -p -m 0644 rel-eng/%{name}-sdn-ovs.conf %{buildroot}%{_unitdir}/${pkgname}-node.service.d/${pkgname}-sdn-ovs.conf
298 302
 done
299 303
 
300
-
301 304
 # Install bash completions
302 305
 install -d -m 755 %{buildroot}/etc/bash_completion.d/
303 306
 install -p -m 644 rel-eng/completions/bash/* %{buildroot}/etc/bash_completion.d/
... ...
@@ -2245,6 +2245,46 @@ func deepCopy_api_HostSubnetList(in sdnapi.HostSubnetList, out *sdnapi.HostSubne
2245 2245
 	return nil
2246 2246
 }
2247 2247
 
2248
+func deepCopy_api_NetNamespace(in sdnapi.NetNamespace, out *sdnapi.NetNamespace, c *conversion.Cloner) error {
2249
+	if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
2250
+		return err
2251
+	} else {
2252
+		out.TypeMeta = newVal.(api.TypeMeta)
2253
+	}
2254
+	if newVal, err := c.DeepCopy(in.ObjectMeta); err != nil {
2255
+		return err
2256
+	} else {
2257
+		out.ObjectMeta = newVal.(api.ObjectMeta)
2258
+	}
2259
+	out.NetName = in.NetName
2260
+	out.NetID = in.NetID
2261
+	return nil
2262
+}
2263
+
2264
+func deepCopy_api_NetNamespaceList(in sdnapi.NetNamespaceList, out *sdnapi.NetNamespaceList, c *conversion.Cloner) error {
2265
+	if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
2266
+		return err
2267
+	} else {
2268
+		out.TypeMeta = newVal.(api.TypeMeta)
2269
+	}
2270
+	if newVal, err := c.DeepCopy(in.ListMeta); err != nil {
2271
+		return err
2272
+	} else {
2273
+		out.ListMeta = newVal.(api.ListMeta)
2274
+	}
2275
+	if in.Items != nil {
2276
+		out.Items = make([]sdnapi.NetNamespace, len(in.Items))
2277
+		for i := range in.Items {
2278
+			if err := deepCopy_api_NetNamespace(in.Items[i], &out.Items[i], c); err != nil {
2279
+				return err
2280
+			}
2281
+		}
2282
+	} else {
2283
+		out.Items = nil
2284
+	}
2285
+	return nil
2286
+}
2287
+
2248 2288
 func deepCopy_api_Parameter(in templateapi.Parameter, out *templateapi.Parameter, c *conversion.Cloner) error {
2249 2289
 	out.Name = in.Name
2250 2290
 	out.Description = in.Description
... ...
@@ -2599,6 +2639,8 @@ func init() {
2599 2599
 		deepCopy_api_ClusterNetworkList,
2600 2600
 		deepCopy_api_HostSubnet,
2601 2601
 		deepCopy_api_HostSubnetList,
2602
+		deepCopy_api_NetNamespace,
2603
+		deepCopy_api_NetNamespaceList,
2602 2604
 		deepCopy_api_Parameter,
2603 2605
 		deepCopy_api_Template,
2604 2606
 		deepCopy_api_TemplateList,
... ...
@@ -138,6 +138,7 @@ func init() {
138 138
 
139 139
 		"ClusterNetwork": true,
140 140
 		"HostSubnet":     true,
141
+		"NetNamespace":   true,
141 142
 	}
142 143
 
143 144
 	// enumerate all supported versions, get the kinds, and register with the mapper how to address our resources
... ...
@@ -2564,6 +2564,44 @@ func convert_api_HostSubnetList_To_v1_HostSubnetList(in *sdnapi.HostSubnetList,
2564 2564
 	return nil
2565 2565
 }
2566 2566
 
2567
+func convert_api_NetNamespace_To_v1_NetNamespace(in *sdnapi.NetNamespace, out *sdnapiv1.NetNamespace, s conversion.Scope) error {
2568
+	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2569
+		defaulting.(func(*sdnapi.NetNamespace))(in)
2570
+	}
2571
+	if err := convert_api_TypeMeta_To_v1_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
2572
+		return err
2573
+	}
2574
+	if err := convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil {
2575
+		return err
2576
+	}
2577
+	out.NetName = in.NetName
2578
+	out.NetID = in.NetID
2579
+	return nil
2580
+}
2581
+
2582
+func convert_api_NetNamespaceList_To_v1_NetNamespaceList(in *sdnapi.NetNamespaceList, out *sdnapiv1.NetNamespaceList, s conversion.Scope) error {
2583
+	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2584
+		defaulting.(func(*sdnapi.NetNamespaceList))(in)
2585
+	}
2586
+	if err := convert_api_TypeMeta_To_v1_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
2587
+		return err
2588
+	}
2589
+	if err := convert_api_ListMeta_To_v1_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil {
2590
+		return err
2591
+	}
2592
+	if in.Items != nil {
2593
+		out.Items = make([]sdnapiv1.NetNamespace, len(in.Items))
2594
+		for i := range in.Items {
2595
+			if err := convert_api_NetNamespace_To_v1_NetNamespace(&in.Items[i], &out.Items[i], s); err != nil {
2596
+				return err
2597
+			}
2598
+		}
2599
+	} else {
2600
+		out.Items = nil
2601
+	}
2602
+	return nil
2603
+}
2604
+
2567 2605
 func convert_v1_ClusterNetwork_To_api_ClusterNetwork(in *sdnapiv1.ClusterNetwork, out *sdnapi.ClusterNetwork, s conversion.Scope) error {
2568 2606
 	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2569 2607
 		defaulting.(func(*sdnapiv1.ClusterNetwork))(in)
... ...
@@ -2641,6 +2679,44 @@ func convert_v1_HostSubnetList_To_api_HostSubnetList(in *sdnapiv1.HostSubnetList
2641 2641
 	return nil
2642 2642
 }
2643 2643
 
2644
+func convert_v1_NetNamespace_To_api_NetNamespace(in *sdnapiv1.NetNamespace, out *sdnapi.NetNamespace, s conversion.Scope) error {
2645
+	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2646
+		defaulting.(func(*sdnapiv1.NetNamespace))(in)
2647
+	}
2648
+	if err := convert_v1_TypeMeta_To_api_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
2649
+		return err
2650
+	}
2651
+	if err := convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil {
2652
+		return err
2653
+	}
2654
+	out.NetName = in.NetName
2655
+	out.NetID = in.NetID
2656
+	return nil
2657
+}
2658
+
2659
+func convert_v1_NetNamespaceList_To_api_NetNamespaceList(in *sdnapiv1.NetNamespaceList, out *sdnapi.NetNamespaceList, s conversion.Scope) error {
2660
+	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2661
+		defaulting.(func(*sdnapiv1.NetNamespaceList))(in)
2662
+	}
2663
+	if err := convert_v1_TypeMeta_To_api_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
2664
+		return err
2665
+	}
2666
+	if err := convert_v1_ListMeta_To_api_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil {
2667
+		return err
2668
+	}
2669
+	if in.Items != nil {
2670
+		out.Items = make([]sdnapi.NetNamespace, len(in.Items))
2671
+		for i := range in.Items {
2672
+			if err := convert_v1_NetNamespace_To_api_NetNamespace(&in.Items[i], &out.Items[i], s); err != nil {
2673
+				return err
2674
+			}
2675
+		}
2676
+	} else {
2677
+		out.Items = nil
2678
+	}
2679
+	return nil
2680
+}
2681
+
2644 2682
 func convert_api_Parameter_To_v1_Parameter(in *templateapi.Parameter, out *templateapiv1.Parameter, s conversion.Scope) error {
2645 2683
 	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2646 2684
 		defaulting.(func(*templateapi.Parameter))(in)
... ...
@@ -3087,6 +3163,8 @@ func init() {
3087 3087
 		convert_api_IsPersonalSubjectAccessReview_To_v1_IsPersonalSubjectAccessReview,
3088 3088
 		convert_api_ListMeta_To_v1_ListMeta,
3089 3089
 		convert_api_LocalObjectReference_To_v1_LocalObjectReference,
3090
+		convert_api_NetNamespaceList_To_v1_NetNamespaceList,
3091
+		convert_api_NetNamespace_To_v1_NetNamespace,
3090 3092
 		convert_api_OAuthAccessTokenList_To_v1_OAuthAccessTokenList,
3091 3093
 		convert_api_OAuthAccessToken_To_v1_OAuthAccessToken,
3092 3094
 		convert_api_OAuthAuthorizeTokenList_To_v1_OAuthAuthorizeTokenList,
... ...
@@ -3163,6 +3241,8 @@ func init() {
3163 3163
 		convert_v1_IsPersonalSubjectAccessReview_To_api_IsPersonalSubjectAccessReview,
3164 3164
 		convert_v1_ListMeta_To_api_ListMeta,
3165 3165
 		convert_v1_LocalObjectReference_To_api_LocalObjectReference,
3166
+		convert_v1_NetNamespaceList_To_api_NetNamespaceList,
3167
+		convert_v1_NetNamespace_To_api_NetNamespace,
3166 3168
 		convert_v1_OAuthAccessTokenList_To_api_OAuthAccessTokenList,
3167 3169
 		convert_v1_OAuthAccessToken_To_api_OAuthAccessToken,
3168 3170
 		convert_v1_OAuthAuthorizeTokenList_To_api_OAuthAuthorizeTokenList,
... ...
@@ -2122,6 +2122,46 @@ func deepCopy_v1_HostSubnetList(in sdnapiv1.HostSubnetList, out *sdnapiv1.HostSu
2122 2122
 	return nil
2123 2123
 }
2124 2124
 
2125
+func deepCopy_v1_NetNamespace(in sdnapiv1.NetNamespace, out *sdnapiv1.NetNamespace, c *conversion.Cloner) error {
2126
+	if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
2127
+		return err
2128
+	} else {
2129
+		out.TypeMeta = newVal.(v1.TypeMeta)
2130
+	}
2131
+	if newVal, err := c.DeepCopy(in.ObjectMeta); err != nil {
2132
+		return err
2133
+	} else {
2134
+		out.ObjectMeta = newVal.(v1.ObjectMeta)
2135
+	}
2136
+	out.NetName = in.NetName
2137
+	out.NetID = in.NetID
2138
+	return nil
2139
+}
2140
+
2141
+func deepCopy_v1_NetNamespaceList(in sdnapiv1.NetNamespaceList, out *sdnapiv1.NetNamespaceList, c *conversion.Cloner) error {
2142
+	if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
2143
+		return err
2144
+	} else {
2145
+		out.TypeMeta = newVal.(v1.TypeMeta)
2146
+	}
2147
+	if newVal, err := c.DeepCopy(in.ListMeta); err != nil {
2148
+		return err
2149
+	} else {
2150
+		out.ListMeta = newVal.(v1.ListMeta)
2151
+	}
2152
+	if in.Items != nil {
2153
+		out.Items = make([]sdnapiv1.NetNamespace, len(in.Items))
2154
+		for i := range in.Items {
2155
+			if err := deepCopy_v1_NetNamespace(in.Items[i], &out.Items[i], c); err != nil {
2156
+				return err
2157
+			}
2158
+		}
2159
+	} else {
2160
+		out.Items = nil
2161
+	}
2162
+	return nil
2163
+}
2164
+
2125 2165
 func deepCopy_v1_Parameter(in templateapiv1.Parameter, out *templateapiv1.Parameter, c *conversion.Cloner) error {
2126 2166
 	out.Name = in.Name
2127 2167
 	out.Description = in.Description
... ...
@@ -2481,6 +2521,8 @@ func init() {
2481 2481
 		deepCopy_v1_ClusterNetworkList,
2482 2482
 		deepCopy_v1_HostSubnet,
2483 2483
 		deepCopy_v1_HostSubnetList,
2484
+		deepCopy_v1_NetNamespace,
2485
+		deepCopy_v1_NetNamespaceList,
2484 2486
 		deepCopy_v1_Parameter,
2485 2487
 		deepCopy_v1_Template,
2486 2488
 		deepCopy_v1_TemplateList,
... ...
@@ -2500,6 +2500,44 @@ func convert_api_HostSubnetList_To_v1beta3_HostSubnetList(in *sdnapi.HostSubnetL
2500 2500
 	return nil
2501 2501
 }
2502 2502
 
2503
+func convert_api_NetNamespace_To_v1beta3_NetNamespace(in *sdnapi.NetNamespace, out *sdnapiv1beta3.NetNamespace, s conversion.Scope) error {
2504
+	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2505
+		defaulting.(func(*sdnapi.NetNamespace))(in)
2506
+	}
2507
+	if err := convert_api_TypeMeta_To_v1beta3_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
2508
+		return err
2509
+	}
2510
+	if err := convert_api_ObjectMeta_To_v1beta3_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil {
2511
+		return err
2512
+	}
2513
+	out.NetName = in.NetName
2514
+	out.NetID = in.NetID
2515
+	return nil
2516
+}
2517
+
2518
+func convert_api_NetNamespaceList_To_v1beta3_NetNamespaceList(in *sdnapi.NetNamespaceList, out *sdnapiv1beta3.NetNamespaceList, s conversion.Scope) error {
2519
+	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2520
+		defaulting.(func(*sdnapi.NetNamespaceList))(in)
2521
+	}
2522
+	if err := convert_api_TypeMeta_To_v1beta3_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
2523
+		return err
2524
+	}
2525
+	if err := convert_api_ListMeta_To_v1beta3_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil {
2526
+		return err
2527
+	}
2528
+	if in.Items != nil {
2529
+		out.Items = make([]sdnapiv1beta3.NetNamespace, len(in.Items))
2530
+		for i := range in.Items {
2531
+			if err := convert_api_NetNamespace_To_v1beta3_NetNamespace(&in.Items[i], &out.Items[i], s); err != nil {
2532
+				return err
2533
+			}
2534
+		}
2535
+	} else {
2536
+		out.Items = nil
2537
+	}
2538
+	return nil
2539
+}
2540
+
2503 2541
 func convert_v1beta3_ClusterNetwork_To_api_ClusterNetwork(in *sdnapiv1beta3.ClusterNetwork, out *sdnapi.ClusterNetwork, s conversion.Scope) error {
2504 2542
 	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2505 2543
 		defaulting.(func(*sdnapiv1beta3.ClusterNetwork))(in)
... ...
@@ -2577,6 +2615,44 @@ func convert_v1beta3_HostSubnetList_To_api_HostSubnetList(in *sdnapiv1beta3.Host
2577 2577
 	return nil
2578 2578
 }
2579 2579
 
2580
+func convert_v1beta3_NetNamespace_To_api_NetNamespace(in *sdnapiv1beta3.NetNamespace, out *sdnapi.NetNamespace, s conversion.Scope) error {
2581
+	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2582
+		defaulting.(func(*sdnapiv1beta3.NetNamespace))(in)
2583
+	}
2584
+	if err := convert_v1beta3_TypeMeta_To_api_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
2585
+		return err
2586
+	}
2587
+	if err := convert_v1beta3_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil {
2588
+		return err
2589
+	}
2590
+	out.NetName = in.NetName
2591
+	out.NetID = in.NetID
2592
+	return nil
2593
+}
2594
+
2595
+func convert_v1beta3_NetNamespaceList_To_api_NetNamespaceList(in *sdnapiv1beta3.NetNamespaceList, out *sdnapi.NetNamespaceList, s conversion.Scope) error {
2596
+	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2597
+		defaulting.(func(*sdnapiv1beta3.NetNamespaceList))(in)
2598
+	}
2599
+	if err := convert_v1beta3_TypeMeta_To_api_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
2600
+		return err
2601
+	}
2602
+	if err := convert_v1beta3_ListMeta_To_api_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil {
2603
+		return err
2604
+	}
2605
+	if in.Items != nil {
2606
+		out.Items = make([]sdnapi.NetNamespace, len(in.Items))
2607
+		for i := range in.Items {
2608
+			if err := convert_v1beta3_NetNamespace_To_api_NetNamespace(&in.Items[i], &out.Items[i], s); err != nil {
2609
+				return err
2610
+			}
2611
+		}
2612
+	} else {
2613
+		out.Items = nil
2614
+	}
2615
+	return nil
2616
+}
2617
+
2580 2618
 func convert_api_Parameter_To_v1beta3_Parameter(in *templateapi.Parameter, out *templateapiv1beta3.Parameter, s conversion.Scope) error {
2581 2619
 	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2582 2620
 		defaulting.(func(*templateapi.Parameter))(in)
... ...
@@ -3021,6 +3097,8 @@ func init() {
3021 3021
 		convert_api_IsPersonalSubjectAccessReview_To_v1beta3_IsPersonalSubjectAccessReview,
3022 3022
 		convert_api_ListMeta_To_v1beta3_ListMeta,
3023 3023
 		convert_api_LocalObjectReference_To_v1beta3_LocalObjectReference,
3024
+		convert_api_NetNamespaceList_To_v1beta3_NetNamespaceList,
3025
+		convert_api_NetNamespace_To_v1beta3_NetNamespace,
3024 3026
 		convert_api_OAuthAccessTokenList_To_v1beta3_OAuthAccessTokenList,
3025 3027
 		convert_api_OAuthAccessToken_To_v1beta3_OAuthAccessToken,
3026 3028
 		convert_api_OAuthAuthorizeTokenList_To_v1beta3_OAuthAuthorizeTokenList,
... ...
@@ -3095,6 +3173,8 @@ func init() {
3095 3095
 		convert_v1beta3_IsPersonalSubjectAccessReview_To_api_IsPersonalSubjectAccessReview,
3096 3096
 		convert_v1beta3_ListMeta_To_api_ListMeta,
3097 3097
 		convert_v1beta3_LocalObjectReference_To_api_LocalObjectReference,
3098
+		convert_v1beta3_NetNamespaceList_To_api_NetNamespaceList,
3099
+		convert_v1beta3_NetNamespace_To_api_NetNamespace,
3098 3100
 		convert_v1beta3_OAuthAccessTokenList_To_api_OAuthAccessTokenList,
3099 3101
 		convert_v1beta3_OAuthAccessToken_To_api_OAuthAccessToken,
3100 3102
 		convert_v1beta3_OAuthAuthorizeTokenList_To_api_OAuthAuthorizeTokenList,
... ...
@@ -2112,6 +2112,46 @@ func deepCopy_v1beta3_HostSubnetList(in sdnapiv1beta3.HostSubnetList, out *sdnap
2112 2112
 	return nil
2113 2113
 }
2114 2114
 
2115
+func deepCopy_v1beta3_NetNamespace(in sdnapiv1beta3.NetNamespace, out *sdnapiv1beta3.NetNamespace, c *conversion.Cloner) error {
2116
+	if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
2117
+		return err
2118
+	} else {
2119
+		out.TypeMeta = newVal.(v1beta3.TypeMeta)
2120
+	}
2121
+	if newVal, err := c.DeepCopy(in.ObjectMeta); err != nil {
2122
+		return err
2123
+	} else {
2124
+		out.ObjectMeta = newVal.(v1beta3.ObjectMeta)
2125
+	}
2126
+	out.NetName = in.NetName
2127
+	out.NetID = in.NetID
2128
+	return nil
2129
+}
2130
+
2131
+func deepCopy_v1beta3_NetNamespaceList(in sdnapiv1beta3.NetNamespaceList, out *sdnapiv1beta3.NetNamespaceList, c *conversion.Cloner) error {
2132
+	if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
2133
+		return err
2134
+	} else {
2135
+		out.TypeMeta = newVal.(v1beta3.TypeMeta)
2136
+	}
2137
+	if newVal, err := c.DeepCopy(in.ListMeta); err != nil {
2138
+		return err
2139
+	} else {
2140
+		out.ListMeta = newVal.(v1beta3.ListMeta)
2141
+	}
2142
+	if in.Items != nil {
2143
+		out.Items = make([]sdnapiv1beta3.NetNamespace, len(in.Items))
2144
+		for i := range in.Items {
2145
+			if err := deepCopy_v1beta3_NetNamespace(in.Items[i], &out.Items[i], c); err != nil {
2146
+				return err
2147
+			}
2148
+		}
2149
+	} else {
2150
+		out.Items = nil
2151
+	}
2152
+	return nil
2153
+}
2154
+
2115 2155
 func deepCopy_v1beta3_Parameter(in templateapiv1beta3.Parameter, out *templateapiv1beta3.Parameter, c *conversion.Cloner) error {
2116 2156
 	out.Name = in.Name
2117 2157
 	out.Description = in.Description
... ...
@@ -2471,6 +2511,8 @@ func init() {
2471 2471
 		deepCopy_v1beta3_ClusterNetworkList,
2472 2472
 		deepCopy_v1beta3_HostSubnet,
2473 2473
 		deepCopy_v1beta3_HostSubnetList,
2474
+		deepCopy_v1beta3_NetNamespace,
2475
+		deepCopy_v1beta3_NetNamespaceList,
2474 2476
 		deepCopy_v1beta3_Parameter,
2475 2477
 		deepCopy_v1beta3_Template,
2476 2478
 		deepCopy_v1beta3_TemplateList,
... ...
@@ -61,6 +61,7 @@ func init() {
61 61
 
62 62
 	Validator.Register(&sdnapi.ClusterNetwork{}, sdnvalidation.ValidateClusterNetwork, sdnvalidation.ValidateClusterNetworkUpdate)
63 63
 	Validator.Register(&sdnapi.HostSubnet{}, sdnvalidation.ValidateHostSubnet, sdnvalidation.ValidateHostSubnetUpdate)
64
+	Validator.Register(&sdnapi.NetNamespace{}, sdnvalidation.ValidateNetNamespace, sdnvalidation.ValidateNetNamespaceUpdate)
64 65
 
65 66
 	Validator.Register(&templateapi.Template{}, templatevalidation.ValidateTemplate, templatevalidation.ValidateTemplateUpdate)
66 67
 
... ...
@@ -67,7 +67,7 @@ var (
67 67
 		BuildGroupName:              {"builds", "buildconfigs", "buildlogs", "buildconfigs/instantiate", "builds/log", "builds/clone", "buildconfigs/webhooks"},
68 68
 		ImageGroupName:              {"imagestreams", "imagestreammappings", "imagestreamtags", "imagestreamimages"},
69 69
 		DeploymentGroupName:         {"deployments", "deploymentconfigs", "generatedeploymentconfigs", "deploymentconfigrollbacks"},
70
-		SDNGroupName:                {"clusternetworks", "hostsubnets"},
70
+		SDNGroupName:                {"clusternetworks", "hostsubnets", "netnamespaces"},
71 71
 		TemplateGroupName:           {"templates", "templateconfigs", "processedtemplates"},
72 72
 		UserGroupName:               {"identities", "users", "useridentitymappings", "groups"},
73 73
 		OAuthGroupName:              {"oauthauthorizetokens", "oauthaccesstokens", "oauthclients", "oauthclientauthorizations"},
... ...
@@ -26,6 +26,7 @@ type Interface interface {
26 26
 	DeploymentConfigsNamespacer
27 27
 	RoutesNamespacer
28 28
 	HostSubnetsInterface
29
+	NetNamespacesInterface
29 30
 	ClusterNetworkingInterface
30 31
 	IdentitiesInterface
31 32
 	UsersInterface
... ...
@@ -100,11 +101,16 @@ func (c *Client) Routes(namespace string) RouteInterface {
100 100
 	return newRoutes(c, namespace)
101 101
 }
102 102
 
103
-// HostSubnet provides a REST client for HostSubnet
103
+// HostSubnets provides a REST client for HostSubnet
104 104
 func (c *Client) HostSubnets() HostSubnetInterface {
105 105
 	return newHostSubnet(c)
106 106
 }
107 107
 
108
+// NetNamespaces provides a REST client for NetNamespace
109
+func (c *Client) NetNamespaces() NetNamespaceInterface {
110
+	return newNetNamespace(c)
111
+}
112
+
108 113
 // ClusterNetwork provides a REST client for ClusterNetworking
109 114
 func (c *Client) ClusterNetwork() ClusterNetworkInterface {
110 115
 	return newClusterNetwork(c)
111 116
new file mode 100644
... ...
@@ -0,0 +1,71 @@
0
+package client
1
+
2
+import (
3
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
4
+
5
+	sdnapi "github.com/openshift/origin/pkg/sdn/api"
6
+)
7
+
8
+// NetNamespaceInterface has methods to work with NetNamespace resources
9
+type NetNamespacesInterface interface {
10
+	NetNamespaces() NetNamespaceInterface
11
+}
12
+
13
+// NetNamespaceInterface exposes methods on NetNamespace resources.
14
+type NetNamespaceInterface interface {
15
+	List() (*sdnapi.NetNamespaceList, error)
16
+	Get(name string) (*sdnapi.NetNamespace, error)
17
+	Create(sub *sdnapi.NetNamespace) (*sdnapi.NetNamespace, error)
18
+	Delete(name string) error
19
+	Watch(resourceVersion string) (watch.Interface, error)
20
+}
21
+
22
+// netNamespace implements NetNamespaceInterface interface
23
+type netNamespace struct {
24
+	r *Client
25
+}
26
+
27
+// newNetNamespace returns a NetNamespace
28
+func newNetNamespace(c *Client) *netNamespace {
29
+	return &netNamespace{
30
+		r: c,
31
+	}
32
+}
33
+
34
+// List returns a list of NetNamespaces that match the label and field selectors.
35
+func (c *netNamespace) List() (result *sdnapi.NetNamespaceList, err error) {
36
+	result = &sdnapi.NetNamespaceList{}
37
+	err = c.r.Get().
38
+		Resource("netNamespaces").
39
+		Do().
40
+		Into(result)
41
+	return
42
+}
43
+
44
+// Get returns information about a particular NetNamespace or an error
45
+func (c *netNamespace) Get(netname string) (result *sdnapi.NetNamespace, err error) {
46
+	result = &sdnapi.NetNamespace{}
47
+	err = c.r.Get().Resource("netNamespaces").Name(netname).Do().Into(result)
48
+	return
49
+}
50
+
51
+// Create creates a new NetNamespace. Returns the server's representation of the NetNamespace and error if one occurs.
52
+func (c *netNamespace) Create(netNamespace *sdnapi.NetNamespace) (result *sdnapi.NetNamespace, err error) {
53
+	result = &sdnapi.NetNamespace{}
54
+	err = c.r.Post().Resource("netNamespaces").Body(netNamespace).Do().Into(result)
55
+	return
56
+}
57
+
58
+// Delete takes the name of the NetNamespace, and returns an error if one occurs during deletion of the NetNamespace
59
+func (c *netNamespace) Delete(name string) error {
60
+	return c.r.Delete().Resource("netNamespaces").Name(name).Do().Error()
61
+}
62
+
63
+// Watch returns a watch.Interface that watches the requested NetNamespaces
64
+func (c *netNamespace) Watch(resourceVersion string) (watch.Interface, error) {
65
+	return c.r.Get().
66
+		Prefix("watch").
67
+		Resource("netNamespaces").
68
+		Param("resourceVersion", resourceVersion).
69
+		Watch()
70
+}
... ...
@@ -109,6 +109,11 @@ func (c *Fake) HostSubnets() client.HostSubnetInterface {
109 109
 	return &FakeHostSubnet{Fake: c}
110 110
 }
111 111
 
112
+// NetNamespaces provides a fake REST client for NetNamespaces
113
+func (c *Fake) NetNamespaces() client.NetNamespaceInterface {
114
+	return &FakeNetNamespace{Fake: c}
115
+}
116
+
112 117
 // ClusterNetwork provides a fake REST client for ClusterNetwork
113 118
 func (c *Fake) ClusterNetwork() client.ClusterNetworkInterface {
114 119
 	return &FakeClusterNetwork{Fake: c}
115 120
new file mode 100644
... ...
@@ -0,0 +1,38 @@
0
+package testclient
1
+
2
+import (
3
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
4
+
5
+	sdnapi "github.com/openshift/origin/pkg/sdn/api"
6
+)
7
+
8
+// FakeNetNamespace implements NetNamespaceInterface. Meant to be embedded into a struct to get a default
9
+// implementation. This makes faking out just the methods you want to test easier.
10
+type FakeNetNamespace struct {
11
+	Fake *Fake
12
+}
13
+
14
+func (c *FakeNetNamespace) List() (*sdnapi.NetNamespaceList, error) {
15
+	obj, err := c.Fake.Invokes(FakeAction{Action: "list-netnamespaces"}, &sdnapi.NetNamespaceList{})
16
+	return obj.(*sdnapi.NetNamespaceList), err
17
+}
18
+
19
+func (c *FakeNetNamespace) Get(name string) (*sdnapi.NetNamespace, error) {
20
+	obj, err := c.Fake.Invokes(FakeAction{Action: "get-netnamespaces"}, &sdnapi.NetNamespace{})
21
+	return obj.(*sdnapi.NetNamespace), err
22
+}
23
+
24
+func (c *FakeNetNamespace) Create(sdn *sdnapi.NetNamespace) (*sdnapi.NetNamespace, error) {
25
+	obj, err := c.Fake.Invokes(FakeAction{Action: "create-netnamespace"}, &sdnapi.NetNamespace{})
26
+	return obj.(*sdnapi.NetNamespace), err
27
+}
28
+
29
+func (c *FakeNetNamespace) Delete(name string) error {
30
+	c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "delete-netnamespace"})
31
+	return nil
32
+}
33
+
34
+func (c *FakeNetNamespace) Watch(resourceVersion string) (watch.Interface, error) {
35
+	c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "watch-netnamespaces"})
36
+	return nil, nil
37
+}
... ...
@@ -59,6 +59,7 @@ var MissingDescriberCoverageExceptions = []reflect.Type{
59 59
 	reflect.TypeOf(&oauthapi.OAuthClient{}),
60 60
 	reflect.TypeOf(&sdnapi.ClusterNetwork{}),
61 61
 	reflect.TypeOf(&sdnapi.HostSubnet{}),
62
+	reflect.TypeOf(&sdnapi.NetNamespace{}),
62 63
 }
63 64
 
64 65
 func TestDescriberCoverage(t *testing.T) {
... ...
@@ -56,6 +56,7 @@ var (
56 56
 	IsPersonalSubjectAccessReviewColumns = []string{"NAME"}
57 57
 
58 58
 	hostSubnetColumns     = []string{"NAME", "HOST", "HOST IP", "SUBNET"}
59
+	netNamespaceColumns   = []string{"NAME", "NETID"}
59 60
 	clusterNetworkColumns = []string{"NAME", "NETWORK", "HOST SUBNET LENGTH"}
60 61
 )
61 62
 
... ...
@@ -121,6 +122,8 @@ func NewHumanReadablePrinter(noHeaders, withNamespace, wide bool, columnLabels [
121 121
 
122 122
 	p.Handler(hostSubnetColumns, printHostSubnet)
123 123
 	p.Handler(hostSubnetColumns, printHostSubnetList)
124
+	p.Handler(netNamespaceColumns, printNetNamespaceList)
125
+	p.Handler(netNamespaceColumns, printNetNamespace)
124 126
 	p.Handler(clusterNetworkColumns, printClusterNetwork)
125 127
 	p.Handler(clusterNetworkColumns, printClusterNetworkList)
126 128
 
... ...
@@ -579,6 +582,20 @@ func printHostSubnetList(list *sdnapi.HostSubnetList, w io.Writer, withNamespace
579 579
 	return nil
580 580
 }
581 581
 
582
+func printNetNamespace(h *sdnapi.NetNamespace, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error {
583
+	_, err := fmt.Fprintf(w, "%s\t%d\n", h.NetName, h.NetID)
584
+	return err
585
+}
586
+
587
+func printNetNamespaceList(list *sdnapi.NetNamespaceList, w io.Writer, withNamespace bool, wide bool, columnLabels []string) error {
588
+	for _, item := range list.Items {
589
+		if err := printNetNamespace(&item, w, withNamespace, wide, columnLabels); err != nil {
590
+			return err
591
+		}
592
+	}
593
+	return nil
594
+}
595
+
582 596
 func printClusterNetwork(n *sdnapi.ClusterNetwork, w io.Writer, withNamespace, wide bool, columnLabels []string) error {
583 597
 	_, err := fmt.Fprintf(w, "%s\t%s\t%d\n", n.Name, n.Network, n.HostSubnetLength)
584 598
 	return err
... ...
@@ -456,6 +456,10 @@ func GetBootstrapClusterRoles() []authorizationapi.ClusterRole {
456 456
 				},
457 457
 				{
458 458
 					Verbs:     util.NewStringSet("get", "list", "watch"),
459
+					Resources: util.NewStringSet("netnamespaces"),
460
+				},
461
+				{
462
+					Verbs:     util.NewStringSet("get", "list", "watch"),
459 463
 					Resources: util.NewStringSet("nodes"),
460 464
 				},
461 465
 				{
... ...
@@ -475,6 +479,10 @@ func GetBootstrapClusterRoles() []authorizationapi.ClusterRole {
475 475
 					Resources: util.NewStringSet("hostsubnets"),
476 476
 				},
477 477
 				{
478
+					Verbs:     util.NewStringSet("get", "list", "watch", "create", "delete"),
479
+					Resources: util.NewStringSet("netnamespaces"),
480
+				},
481
+				{
478 482
 					Verbs:     util.NewStringSet("get", "list", "watch"),
479 483
 					Resources: util.NewStringSet("nodes"),
480 484
 				},
... ...
@@ -56,6 +56,7 @@ import (
56 56
 	routeregistry "github.com/openshift/origin/pkg/route/registry/route"
57 57
 	clusternetworketcd "github.com/openshift/origin/pkg/sdn/registry/clusternetwork/etcd"
58 58
 	hostsubnetetcd "github.com/openshift/origin/pkg/sdn/registry/hostsubnet/etcd"
59
+	netnamespaceetcd "github.com/openshift/origin/pkg/sdn/registry/netnamespace/etcd"
59 60
 	"github.com/openshift/origin/pkg/service"
60 61
 	templateregistry "github.com/openshift/origin/pkg/template/registry"
61 62
 	templateetcd "github.com/openshift/origin/pkg/template/registry/etcd"
... ...
@@ -310,6 +311,7 @@ func (c *MasterConfig) GetRestStorage() map[string]rest.Storage {
310 310
 
311 311
 	routeEtcd := routeetcd.New(c.EtcdHelper)
312 312
 	hostSubnetStorage := hostsubnetetcd.NewREST(c.EtcdHelper)
313
+	netNamespaceStorage := netnamespaceetcd.NewREST(c.EtcdHelper)
313 314
 	clusterNetworkStorage := clusternetworketcd.NewREST(c.EtcdHelper)
314 315
 
315 316
 	userStorage := useretcd.NewREST(c.EtcdHelper)
... ...
@@ -425,6 +427,7 @@ func (c *MasterConfig) GetRestStorage() map[string]rest.Storage {
425 425
 		"projectRequests": projectRequestStorage,
426 426
 
427 427
 		"hostSubnets":     hostSubnetStorage,
428
+		"netNamespaces":   netNamespaceStorage,
428 429
 		"clusterNetworks": clusterNetworkStorage,
429 430
 
430 431
 		"users":                userStorage,
... ...
@@ -39,7 +39,8 @@ import (
39 39
 	configapi "github.com/openshift/origin/pkg/cmd/server/api"
40 40
 	"github.com/openshift/origin/pkg/cmd/server/bootstrappolicy"
41 41
 	serviceaccountcontrollers "github.com/openshift/origin/pkg/serviceaccounts/controllers"
42
-	"github.com/openshift/origin/plugins/osdn"
42
+	"github.com/openshift/origin/plugins/osdn/flatsdn"
43
+	"github.com/openshift/origin/plugins/osdn/multitenant"
43 44
 )
44 45
 
45 46
 // RunProjectAuthorizationCache starts the project authorization cache
... ...
@@ -306,8 +307,11 @@ func (c *MasterConfig) RunDeploymentImageChangeTriggerController() {
306 306
 // RunSDNController runs openshift-sdn if the said network plugin is provided
307 307
 func (c *MasterConfig) RunSDNController() {
308 308
 	osclient, kclient := c.SDNControllerClients()
309
-	if c.Options.NetworkConfig.NetworkPluginName == osdn.NetworkPluginName() {
310
-		osdn.Master(osclient, kclient, c.Options.NetworkConfig.ClusterNetworkCIDR, c.Options.NetworkConfig.HostSubnetLength)
309
+	switch c.Options.NetworkConfig.NetworkPluginName {
310
+	case flatsdn.NetworkPluginName():
311
+		flatsdn.Master(osclient, kclient, c.Options.NetworkConfig.ClusterNetworkCIDR, c.Options.NetworkConfig.HostSubnetLength)
312
+	case multitenant.NetworkPluginName():
313
+		multitenant.Master(osclient, kclient, c.Options.NetworkConfig.ClusterNetworkCIDR, c.Options.NetworkConfig.HostSubnetLength)
311 314
 	}
312 315
 }
313 316
 
... ...
@@ -13,7 +13,8 @@ import (
13 13
 
14 14
 	kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
15 15
 	"github.com/openshift/origin/pkg/cmd/server/kubernetes"
16
-	"github.com/openshift/origin/plugins/osdn"
16
+	"github.com/openshift/origin/plugins/osdn/flatsdn"
17
+	"github.com/openshift/origin/plugins/osdn/multitenant"
17 18
 
18 19
 	"github.com/openshift/origin/pkg/cmd/server/admin"
19 20
 	configapi "github.com/openshift/origin/pkg/cmd/server/api"
... ...
@@ -242,17 +243,23 @@ func (o NodeOptions) IsRunFromConfig() bool {
242 242
 }
243 243
 
244 244
 func RunSDNController(config *kubernetes.NodeConfig, nodeConfig configapi.NodeConfig) {
245
-	if nodeConfig.NetworkPluginName != osdn.NetworkPluginName() {
246
-		return
247
-	}
248
-
249 245
 	oclient, _, err := configapi.GetOpenShiftClient(nodeConfig.MasterKubeConfig)
250 246
 	if err != nil {
251 247
 		glog.Fatal("Failed to get kube client for SDN")
252 248
 	}
253
-	ch := make(chan struct{})
254
-	config.KubeletConfig.StartUpdates = ch
255
-	go osdn.Node(oclient, config.Client, nodeConfig.NodeName, "", ch)
249
+
250
+	switch nodeConfig.NetworkPluginName {
251
+	case flatsdn.NetworkPluginName():
252
+		ch := make(chan struct{})
253
+		config.KubeletConfig.StartUpdates = ch
254
+		go flatsdn.Node(oclient, config.Client, nodeConfig.NodeName, "", ch)
255
+	case multitenant.NetworkPluginName():
256
+		ch := make(chan struct{})
257
+		config.KubeletConfig.StartUpdates = ch
258
+		plugin := multitenant.GetKubeNetworkPlugin()
259
+		config.KubeletConfig.NetworkPlugins = append(config.KubeletConfig.NetworkPlugins, plugin)
260
+		go multitenant.Node(oclient, config.Client, nodeConfig.NodeName, "", ch, plugin)
261
+	}
256 262
 }
257 263
 
258 264
 func StartNode(nodeConfig configapi.NodeConfig) error {
... ...
@@ -10,6 +10,8 @@ func init() {
10 10
 		&ClusterNetworkList{},
11 11
 		&HostSubnet{},
12 12
 		&HostSubnetList{},
13
+		&NetNamespace{},
14
+		&NetNamespaceList{},
13 15
 	)
14 16
 }
15 17
 
... ...
@@ -17,3 +19,5 @@ func (*ClusterNetwork) IsAnAPIObject()     {}
17 17
 func (*ClusterNetworkList) IsAnAPIObject() {}
18 18
 func (*HostSubnet) IsAnAPIObject()         {}
19 19
 func (*HostSubnetList) IsAnAPIObject()     {}
20
+func (*NetNamespace) IsAnAPIObject()       {}
21
+func (*NetNamespaceList) IsAnAPIObject()   {}
... ...
@@ -35,3 +35,19 @@ type HostSubnetList struct {
35 35
 	kapi.ListMeta
36 36
 	Items []HostSubnet
37 37
 }
38
+
39
+// NetNamespace holds the network id against its name
40
+type NetNamespace struct {
41
+	kapi.TypeMeta
42
+	kapi.ObjectMeta
43
+
44
+	NetName string
45
+	NetID   uint
46
+}
47
+
48
+// NetNamespaceList is a collection of NetNamespaces
49
+type NetNamespaceList struct {
50
+	kapi.TypeMeta
51
+	kapi.ListMeta
52
+	Items []NetNamespace
53
+}
... ...
@@ -10,6 +10,8 @@ func init() {
10 10
 		&ClusterNetworkList{},
11 11
 		&HostSubnet{},
12 12
 		&HostSubnetList{},
13
+		&NetNamespace{},
14
+		&NetNamespaceList{},
13 15
 	)
14 16
 }
15 17
 
... ...
@@ -17,3 +19,5 @@ func (*ClusterNetwork) IsAnAPIObject()     {}
17 17
 func (*ClusterNetworkList) IsAnAPIObject() {}
18 18
 func (*HostSubnet) IsAnAPIObject()         {}
19 19
 func (*HostSubnetList) IsAnAPIObject()     {}
20
+func (*NetNamespace) IsAnAPIObject()       {}
21
+func (*NetNamespaceList) IsAnAPIObject()   {}
... ...
@@ -35,3 +35,19 @@ type HostSubnetList struct {
35 35
 	kapi.ListMeta `json:"metadata,omitempty"`
36 36
 	Items         []HostSubnet `json:"items" description:"list of host subnets"`
37 37
 }
38
+
39
+// NetNamespace encapsulates the inputs needed to define a unique network namespace on the cluster
40
+type NetNamespace struct {
41
+	kapi.TypeMeta   `json:",inline"`
42
+	kapi.ObjectMeta `json:"metadata,omitempty"`
43
+
44
+	NetName string `json:"netname" description:"Name of the network namespace."`
45
+	NetID   uint   `json:"netid" description:"NetID of the network namespace assigned to each overlay network packet."`
46
+}
47
+
48
+// NetNamespaceList is a collection of NetNamespaces
49
+type NetNamespaceList struct {
50
+	kapi.TypeMeta `json:",inline"`
51
+	kapi.ListMeta `json:"metadata,omitempty"`
52
+	Items         []NetNamespace `json:"items" description:"list of net namespaces"`
53
+}
... ...
@@ -10,6 +10,8 @@ func init() {
10 10
 		&ClusterNetworkList{},
11 11
 		&HostSubnet{},
12 12
 		&HostSubnetList{},
13
+		&NetNamespace{},
14
+		&NetNamespaceList{},
13 15
 	)
14 16
 }
15 17
 
... ...
@@ -17,3 +19,5 @@ func (*ClusterNetwork) IsAnAPIObject()     {}
17 17
 func (*ClusterNetworkList) IsAnAPIObject() {}
18 18
 func (*HostSubnet) IsAnAPIObject()         {}
19 19
 func (*HostSubnetList) IsAnAPIObject()     {}
20
+func (*NetNamespace) IsAnAPIObject()       {}
21
+func (*NetNamespaceList) IsAnAPIObject()   {}
... ...
@@ -35,3 +35,19 @@ type HostSubnetList struct {
35 35
 	kapi.ListMeta `json:"metadata,omitempty"`
36 36
 	Items         []HostSubnet `json:"items"`
37 37
 }
38
+
39
+// NetNamespace encapsulates the inputs needed to define a unique network namespace on the cluster
40
+type NetNamespace struct {
41
+	kapi.TypeMeta   `json:",inline"`
42
+	kapi.ObjectMeta `json:"metadata,omitempty"`
43
+
44
+	NetName string `json:"netname" description:"Name of the network namespace."`
45
+	NetID   uint   `json:"netid" description:"NetID of the network namespace assigned to each overlay network packet."`
46
+}
47
+
48
+// NetNamespaceList is a collection of NetNamespaces
49
+type NetNamespaceList struct {
50
+	kapi.TypeMeta `json:",inline"`
51
+	kapi.ListMeta `json:"metadata,omitempty"`
52
+	Items         []NetNamespace `json:"items" description:"list of net namespaces"`
53
+}
... ...
@@ -65,3 +65,25 @@ func ValidateHostSubnetUpdate(obj *sdnapi.HostSubnet, old *sdnapi.HostSubnet) fi
65 65
 
66 66
 	return allErrs
67 67
 }
68
+
69
+// ValidateNetNamespace tests fields for a greater-than-zero NetID
70
+func ValidateNetNamespace(netnamespace *sdnapi.NetNamespace) fielderrors.ValidationErrorList {
71
+	allErrs := fielderrors.ValidationErrorList{}
72
+	allErrs = append(allErrs, validation.ValidateObjectMeta(&netnamespace.ObjectMeta, false, oapi.MinimalNameRequirements).Prefix("metadata")...)
73
+
74
+	if netnamespace.NetID < 0 {
75
+		allErrs = append(allErrs, fielderrors.NewFieldInvalid("netID", netnamespace.NetID, "invalid Net ID: cannot be negative"))
76
+	}
77
+	return allErrs
78
+}
79
+
80
+func ValidateNetNamespaceUpdate(obj *sdnapi.NetNamespace, old *sdnapi.NetNamespace) fielderrors.ValidationErrorList {
81
+	allErrs := fielderrors.ValidationErrorList{}
82
+	allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&obj.ObjectMeta, &old.ObjectMeta).Prefix("metadata")...)
83
+
84
+	if obj.NetID != old.NetID {
85
+		allErrs = append(allErrs, fielderrors.NewFieldInvalid("netid", obj.NetID, "cannot change the NetID midflight."))
86
+	}
87
+
88
+	return allErrs
89
+}
68 90
new file mode 100644
... ...
@@ -0,0 +1,49 @@
0
+package etcd
1
+
2
+import (
3
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
4
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
5
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
6
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic"
7
+	etcdgeneric "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic/etcd"
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
10
+
11
+	"github.com/openshift/origin/pkg/sdn/api"
12
+	"github.com/openshift/origin/pkg/sdn/registry/netnamespace"
13
+)
14
+
15
+// rest implements a RESTStorage for sdn against etcd
16
+type REST struct {
17
+	etcdgeneric.Etcd
18
+}
19
+
20
+const etcdPrefix = "/registry/sdnnetnamespaces"
21
+
22
+// NewREST returns a RESTStorage object that will work against netnamespaces
23
+func NewREST(h tools.EtcdHelper) *REST {
24
+	store := &etcdgeneric.Etcd{
25
+		NewFunc:     func() runtime.Object { return &api.NetNamespace{} },
26
+		NewListFunc: func() runtime.Object { return &api.NetNamespaceList{} },
27
+		KeyRootFunc: func(ctx kapi.Context) string {
28
+			return etcdPrefix
29
+		},
30
+		KeyFunc: func(ctx kapi.Context, name string) (string, error) {
31
+			return (etcdPrefix + "/" + name), nil
32
+		},
33
+		ObjectNameFunc: func(obj runtime.Object) (string, error) {
34
+			return obj.(*api.NetNamespace).NetName, nil
35
+		},
36
+		PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher {
37
+			return netnamespace.MatchNetNamespace(label, field)
38
+		},
39
+		EndpointName: "netnamespace",
40
+
41
+		Helper: h,
42
+	}
43
+
44
+	store.CreateStrategy = netnamespace.Strategy
45
+	store.UpdateStrategy = netnamespace.Strategy
46
+
47
+	return &REST{*store}
48
+}
0 49
new file mode 100644
... ...
@@ -0,0 +1,77 @@
0
+package netnamespace
1
+
2
+import (
3
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
4
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
5
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
6
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
7
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
8
+
9
+	"github.com/openshift/origin/pkg/sdn/api"
10
+)
11
+
12
+// Registry is an interface implemented by things that know how to store sdn objects.
13
+type Registry interface {
14
+	// ListNetNamespaces obtains a list of NetNamespaces
15
+	ListNetNamespaces(ctx kapi.Context) (*api.NetNamespaceList, error)
16
+	// GetNetNamespace returns a specific NetNamespace
17
+	GetNetNamespace(ctx kapi.Context, name string) (*api.NetNamespace, error)
18
+	// CreateNetNamespace creates a NetNamespace
19
+	CreateNetNamespace(ctx kapi.Context, nn *api.NetNamespace) (*api.NetNamespace, error)
20
+	// DeleteNetNamespace deletes a netnamespace
21
+	DeleteNetNamespace(ctx kapi.Context, name string) error
22
+}
23
+
24
+// Storage is an interface for a standard REST Storage backend
25
+// TODO: move me somewhere common
26
+type Storage interface {
27
+	rest.Lister
28
+	rest.Getter
29
+
30
+	Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error)
31
+	Update(ctx kapi.Context, obj runtime.Object) (runtime.Object, bool, error)
32
+	Delete(ctx kapi.Context, name string, opts *kapi.DeleteOptions) (runtime.Object, error)
33
+}
34
+
35
+// storage puts strong typing around storage calls
36
+type storage struct {
37
+	Storage
38
+}
39
+
40
+// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
41
+// types will panic.
42
+func NewRegistry(s Storage) Registry {
43
+	return &storage{s}
44
+}
45
+
46
+func (s *storage) ListNetNamespaces(ctx kapi.Context) (*api.NetNamespaceList, error) {
47
+	obj, err := s.List(ctx, labels.Everything(), fields.Everything())
48
+	if err != nil {
49
+		return nil, err
50
+	}
51
+	return obj.(*api.NetNamespaceList), nil
52
+}
53
+
54
+func (s *storage) GetNetNamespace(ctx kapi.Context, name string) (*api.NetNamespace, error) {
55
+	obj, err := s.Get(ctx, name)
56
+	if err != nil {
57
+		return nil, err
58
+	}
59
+	return obj.(*api.NetNamespace), nil
60
+}
61
+
62
+func (s *storage) CreateNetNamespace(ctx kapi.Context, nn *api.NetNamespace) (*api.NetNamespace, error) {
63
+	obj, err := s.Create(ctx, nn)
64
+	if err != nil {
65
+		return nil, err
66
+	}
67
+	return obj.(*api.NetNamespace), nil
68
+}
69
+
70
+func (s *storage) DeleteNetNamespace(ctx kapi.Context, name string) error {
71
+	_, err := s.Delete(ctx, name, nil)
72
+	if err != nil {
73
+		return err
74
+	}
75
+	return nil
76
+}
0 77
new file mode 100644
... ...
@@ -0,0 +1,68 @@
0
+package netnamespace
1
+
2
+import (
3
+	"fmt"
4
+
5
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
6
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
7
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic"
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
10
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/fielderrors"
11
+
12
+	"github.com/openshift/origin/pkg/sdn/api"
13
+	"github.com/openshift/origin/pkg/sdn/api/validation"
14
+)
15
+
16
+// sdnStrategy implements behavior for NetNamespaces
17
+type sdnStrategy struct {
18
+	runtime.ObjectTyper
19
+}
20
+
21
+// Strategy is the default logic that applies when creating and updating NetNamespace
22
+// objects via the REST API.
23
+var Strategy = sdnStrategy{kapi.Scheme}
24
+
25
+func (sdnStrategy) PrepareForUpdate(obj, old runtime.Object) {}
26
+
27
+// NamespaceScoped is false for sdns
28
+func (sdnStrategy) NamespaceScoped() bool {
29
+	return false
30
+}
31
+
32
+func (sdnStrategy) GenerateName(base string) string {
33
+	return base
34
+}
35
+
36
+func (sdnStrategy) PrepareForCreate(obj runtime.Object) {
37
+}
38
+
39
+// Validate validates a new NetNamespace
40
+func (sdnStrategy) Validate(ctx kapi.Context, obj runtime.Object) fielderrors.ValidationErrorList {
41
+	return validation.ValidateNetNamespace(obj.(*api.NetNamespace))
42
+}
43
+
44
+// AllowCreateOnUpdate is false for NetNamespace
45
+func (sdnStrategy) AllowCreateOnUpdate() bool {
46
+	return false
47
+}
48
+
49
+func (sdnStrategy) AllowUnconditionalUpdate() bool {
50
+	return false
51
+}
52
+
53
+// ValidateUpdate is the default update validation for a NetNamespace
54
+func (sdnStrategy) ValidateUpdate(ctx kapi.Context, obj, old runtime.Object) fielderrors.ValidationErrorList {
55
+	return validation.ValidateNetNamespaceUpdate(obj.(*api.NetNamespace), old.(*api.NetNamespace))
56
+}
57
+
58
+// MatchNetNamespace returns a generic matcher for a given label and field selector.
59
+func MatchNetNamespace(label labels.Selector, field fields.Selector) generic.Matcher {
60
+	return generic.MatcherFunc(func(obj runtime.Object) (bool, error) {
61
+		_, ok := obj.(*api.NetNamespace)
62
+		if !ok {
63
+			return false, fmt.Errorf("not a NetNamespace")
64
+		}
65
+		return true, nil
66
+	})
67
+}
0 68
new file mode 100644
... ...
@@ -0,0 +1,49 @@
0
+package flatsdn
1
+
2
+import (
3
+	"github.com/golang/glog"
4
+	"strings"
5
+
6
+	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
7
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec"
8
+
9
+	"github.com/openshift/openshift-sdn/ovssubnet"
10
+	osclient "github.com/openshift/origin/pkg/client"
11
+	"github.com/openshift/origin/plugins/osdn"
12
+)
13
+
14
+func NetworkPluginName() string {
15
+	return "redhat/openshift-ovs-subnet"
16
+}
17
+
18
+func Master(osClient *osclient.Client, kClient *kclient.Client, clusterNetwork string, clusterNetworkLength uint) {
19
+	osdnInterface := osdn.NewOsdnRegistryInterface(osClient, kClient)
20
+
21
+	// get hostname from the gateway
22
+	output, err := exec.New().Command("hostname", "-f").CombinedOutput()
23
+	if err != nil {
24
+		glog.Fatalf("SDN initialization failed: %v", err)
25
+	}
26
+	host := strings.TrimSpace(string(output))
27
+
28
+	kc, err := ovssubnet.NewKubeController(&osdnInterface, host, "", nil)
29
+	if err != nil {
30
+		glog.Fatalf("SDN initialization failed: %v", err)
31
+	}
32
+	err = kc.StartMaster(false, clusterNetwork, clusterNetworkLength)
33
+	if err != nil {
34
+		glog.Fatalf("SDN initialization failed: %v", err)
35
+	}
36
+}
37
+
38
+func Node(osClient *osclient.Client, kClient *kclient.Client, hostname string, publicIP string, ready chan struct{}) {
39
+	osdnInterface := osdn.NewOsdnRegistryInterface(osClient, kClient)
40
+	kc, err := ovssubnet.NewKubeController(&osdnInterface, hostname, publicIP, ready)
41
+	if err != nil {
42
+		glog.Fatalf("SDN initialization failed: %v", err)
43
+	}
44
+	err = kc.StartNode(false, false)
45
+	if err != nil {
46
+		glog.Fatalf("SDN Node failed: %v", err)
47
+	}
48
+}
0 49
new file mode 100644
... ...
@@ -0,0 +1,55 @@
0
+package multitenant
1
+
2
+import (
3
+	"github.com/golang/glog"
4
+	"strings"
5
+
6
+	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
7
+	knetwork "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/network"
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec"
9
+
10
+	"github.com/openshift/openshift-sdn/ovssubnet"
11
+	osclient "github.com/openshift/origin/pkg/client"
12
+	"github.com/openshift/origin/plugins/osdn"
13
+)
14
+
15
+func NetworkPluginName() string {
16
+	return "redhat/openshift-ovs-multitenant"
17
+}
18
+
19
+func Master(osClient *osclient.Client, kClient *kclient.Client, clusterNetwork string, clusterNetworkLength uint) {
20
+	osdnInterface := osdn.NewOsdnRegistryInterface(osClient, kClient)
21
+
22
+	// get hostname from the gateway
23
+	output, err := exec.New().Command("hostname", "-f").CombinedOutput()
24
+	if err != nil {
25
+		glog.Fatalf("SDN initialization failed: %v", err)
26
+	}
27
+	host := strings.TrimSpace(string(output))
28
+
29
+	kc, err := ovssubnet.NewMultitenantController(&osdnInterface, host, "", nil)
30
+	if err != nil {
31
+		glog.Fatalf("SDN initialization failed: %v", err)
32
+	}
33
+	err = kc.StartMaster(false, clusterNetwork, clusterNetworkLength)
34
+	if err != nil {
35
+		glog.Fatalf("SDN initialization failed: %v", err)
36
+	}
37
+}
38
+
39
+func Node(osClient *osclient.Client, kClient *kclient.Client, hostname string, publicIP string, ready chan struct{}, plugin knetwork.NetworkPlugin) {
40
+	mp, ok := plugin.(*MultitenantPlugin)
41
+	if !ok {
42
+		glog.Fatalf("Failed to type cast provided plugin to a multitenant type plugin")
43
+	}
44
+	osdnInterface := osdn.NewOsdnRegistryInterface(osClient, kClient)
45
+	kc, err := ovssubnet.NewMultitenantController(&osdnInterface, hostname, publicIP, ready)
46
+	if err != nil {
47
+		glog.Fatalf("SDN initialization failed: %v", err)
48
+	}
49
+	mp.OvsController = kc
50
+	err = kc.StartNode(false, false)
51
+	if err != nil {
52
+		glog.Fatalf("SDN Node failed: %v", err)
53
+	}
54
+}
0 55
new file mode 100644
... ...
@@ -0,0 +1,66 @@
0
+package multitenant
1
+
2
+import (
3
+	"github.com/golang/glog"
4
+	"strconv"
5
+
6
+	knetwork "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/network"
7
+	kubeletTypes "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/types"
8
+	utilexec "github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec"
9
+	"github.com/openshift/openshift-sdn/ovssubnet"
10
+)
11
+
12
+const (
13
+	initCmd     = "init"
14
+	setUpCmd    = "setup"
15
+	tearDownCmd = "teardown"
16
+)
17
+
18
+type MultitenantPlugin struct {
19
+	host          knetwork.Host
20
+	OvsController *ovssubnet.OvsController
21
+}
22
+
23
+func GetKubeNetworkPlugin() knetwork.NetworkPlugin {
24
+	return &MultitenantPlugin{}
25
+}
26
+
27
+func (plugin *MultitenantPlugin) getExecutable() string {
28
+	return "openshift-ovs-multitenant"
29
+}
30
+
31
+func (plugin *MultitenantPlugin) getVnid(namespace string) (uint, error) {
32
+	// get vnid for the namespace
33
+	vnid, ok := plugin.OvsController.VnidMap[namespace]
34
+	if !ok {
35
+		// vnid does not exist for this pod, set it to zero (or error?)
36
+		vnid = 0
37
+	}
38
+	return vnid, nil
39
+}
40
+
41
+func (plugin *MultitenantPlugin) Init(host knetwork.Host) error {
42
+	plugin.host = host
43
+	return nil
44
+}
45
+
46
+func (plugin *MultitenantPlugin) Name() string {
47
+	return NetworkPluginName()
48
+}
49
+
50
+func (plugin *MultitenantPlugin) SetUpPod(namespace string, name string, id kubeletTypes.DockerID) error {
51
+	vnid, err := plugin.getVnid(namespace)
52
+	if err != nil {
53
+		return err
54
+	}
55
+	out, err := utilexec.New().Command(plugin.getExecutable(), setUpCmd, namespace, name, string(id), strconv.FormatUint(uint64(vnid), 10)).CombinedOutput()
56
+	glog.V(5).Infof("SetUpPod 'multitenant' network plugin output: %s, %v", string(out), err)
57
+	return err
58
+}
59
+
60
+func (plugin *MultitenantPlugin) TearDownPod(namespace string, name string, id kubeletTypes.DockerID) error {
61
+	vnid, err := plugin.getVnid(namespace)
62
+	out, err := utilexec.New().Command(plugin.getExecutable(), tearDownCmd, namespace, name, string(id), strconv.FormatUint(uint64(vnid), 10)).CombinedOutput()
63
+	glog.V(5).Infof("TearDownPod 'multitenant' network plugin output: %s, %v", string(out), err)
64
+	return err
65
+}
... ...
@@ -2,8 +2,6 @@ package osdn
2 2
 
3 3
 import (
4 4
 	"fmt"
5
-	"github.com/golang/glog"
6
-	"strings"
7 5
 	"time"
8 6
 
9 7
 	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
... ...
@@ -12,11 +10,9 @@ import (
12 12
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
13 13
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
14 14
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
15
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec"
16 15
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
17 16
 
18
-	"github.com/openshift/openshift-sdn/ovssubnet"
19
-	osdnapi "github.com/openshift/openshift-sdn/pkg/api"
17
+	osdnapi "github.com/openshift/openshift-sdn/ovssubnet/api"
20 18
 
21 19
 	osclient "github.com/openshift/origin/pkg/client"
22 20
 	oscache "github.com/openshift/origin/pkg/client/cache"
... ...
@@ -28,40 +24,7 @@ type OsdnRegistryInterface struct {
28 28
 	kClient kclient.Interface
29 29
 }
30 30
 
31
-func NetworkPluginName() string {
32
-	return "redhat/openshift-ovs-subnet"
33
-}
34
-
35
-func Master(osClient *osclient.Client, kClient *kclient.Client, clusterNetwork string, clusterNetworkLength uint) {
36
-	osdnInterface := newOsdnRegistryInterface(osClient, kClient)
37
-
38
-	// get hostname from the gateway
39
-	output, err := exec.New().Command("hostname", "-f").CombinedOutput()
40
-	if err != nil {
41
-		glog.Fatalf("SDN initialization failed: %v", err)
42
-	}
43
-	host := strings.TrimSpace(string(output))
44
-
45
-	kc, err := ovssubnet.NewKubeController(&osdnInterface, host, "", nil)
46
-	if err != nil {
47
-		glog.Fatalf("SDN initialization failed: %v", err)
48
-	}
49
-	err = kc.StartMaster(false, clusterNetwork, clusterNetworkLength)
50
-	if err != nil {
51
-		glog.Fatalf("SDN initialization failed: %v", err)
52
-	}
53
-}
54
-
55
-func Node(osClient *osclient.Client, kClient *kclient.Client, hostname string, publicIP string, ready chan struct{}) {
56
-	osdnInterface := newOsdnRegistryInterface(osClient, kClient)
57
-	kc, err := ovssubnet.NewKubeController(&osdnInterface, hostname, publicIP, ready)
58
-	if err != nil {
59
-		glog.Fatalf("SDN initialization failed: %v", err)
60
-	}
61
-	kc.StartNode(false, false)
62
-}
63
-
64
-func newOsdnRegistryInterface(osClient *osclient.Client, kClient *kclient.Client) OsdnRegistryInterface {
31
+func NewOsdnRegistryInterface(osClient *osclient.Client, kClient *kclient.Client) OsdnRegistryInterface {
65 32
 	return OsdnRegistryInterface{osClient, kClient}
66 33
 }
67 34
 
... ...
@@ -228,3 +191,109 @@ func (oi *OsdnRegistryInterface) CheckEtcdIsAlive(seconds uint64) bool {
228 228
 	// always assumed to be true as we run through the apiserver
229 229
 	return true
230 230
 }
231
+
232
+func (oi *OsdnRegistryInterface) WatchNamespaces(receiver chan *osdnapi.NamespaceEvent, stop chan bool) error {
233
+	nsEventQueue := oscache.NewEventQueue(cache.MetaNamespaceKeyFunc)
234
+	listWatch := &cache.ListWatch{
235
+		ListFunc: func() (runtime.Object, error) {
236
+			return oi.kClient.Namespaces().List(labels.Everything(), fields.Everything())
237
+		},
238
+		WatchFunc: func(resourceVersion string) (watch.Interface, error) {
239
+			return oi.kClient.Namespaces().Watch(labels.Everything(), fields.Everything(), resourceVersion)
240
+		},
241
+	}
242
+	cache.NewReflector(listWatch, &kapi.Namespace{}, nsEventQueue, 4*time.Minute).Run()
243
+
244
+	for {
245
+		eventType, obj, err := nsEventQueue.Pop()
246
+		if err != nil {
247
+			return err
248
+		}
249
+		switch eventType {
250
+		case watch.Added:
251
+			// we should ignore the modified event because status updates cause unnecessary noise
252
+			// the only time we would care about modified would be if the minion changes its IP address
253
+			// and hence all nodes need to update their vtep entries for the respective subnet
254
+			// create minionEvent
255
+			ns := obj.(*kapi.Namespace)
256
+			receiver <- &osdnapi.NamespaceEvent{Type: osdnapi.Added, Name: ns.ObjectMeta.Name}
257
+		case watch.Deleted:
258
+			// TODO: There is a chance that a Delete event will not get triggered.
259
+			// Need to use a periodic sync loop that lists and compares.
260
+			ns := obj.(*kapi.Namespace)
261
+			receiver <- &osdnapi.NamespaceEvent{Type: osdnapi.Deleted, Name: ns.ObjectMeta.Name}
262
+		}
263
+	}
264
+	return nil
265
+}
266
+
267
+func (oi *OsdnRegistryInterface) WatchNetNamespaces(receiver chan *osdnapi.NetNamespaceEvent, stop chan bool) error {
268
+	netNsEventQueue := oscache.NewEventQueue(cache.MetaNamespaceKeyFunc)
269
+	listWatch := &cache.ListWatch{
270
+		ListFunc: func() (runtime.Object, error) {
271
+			return oi.oClient.NetNamespaces().List()
272
+		},
273
+		WatchFunc: func(resourceVersion string) (watch.Interface, error) {
274
+			return oi.oClient.NetNamespaces().Watch(resourceVersion)
275
+		},
276
+	}
277
+	cache.NewReflector(listWatch, &api.NetNamespace{}, netNsEventQueue, 4*time.Minute).Run()
278
+
279
+	for {
280
+		eventType, obj, err := netNsEventQueue.Pop()
281
+		if err != nil {
282
+			return err
283
+		}
284
+		switch eventType {
285
+		case watch.Added:
286
+			// we should ignore the modified event because status updates cause unnecessary noise
287
+			// the only time we would care about modified would be if the minion changes its IP address
288
+			// and hence all nodes need to update their vtep entries for the respective subnet
289
+			// create minionEvent
290
+			netns := obj.(*api.NetNamespace)
291
+			receiver <- &osdnapi.NetNamespaceEvent{Type: osdnapi.Added, Name: netns.NetName, NetID: netns.NetID}
292
+		case watch.Deleted:
293
+			// TODO: There is a chance that a Delete event will not get triggered.
294
+			// Need to use a periodic sync loop that lists and compares.
295
+			netns := obj.(*api.NetNamespace)
296
+			receiver <- &osdnapi.NetNamespaceEvent{Type: osdnapi.Deleted, Name: netns.NetName}
297
+		}
298
+	}
299
+	return nil
300
+}
301
+
302
+func (oi *OsdnRegistryInterface) GetNetNamespaces() ([]osdnapi.NetNamespace, error) {
303
+	netNamespaceList, err := oi.oClient.NetNamespaces().List()
304
+	if err != nil {
305
+		return nil, err
306
+	}
307
+	// convert api.NetNamespace to osdnapi.NetNamespace
308
+	nsList := make([]osdnapi.NetNamespace, 0)
309
+	for _, netns := range netNamespaceList.Items {
310
+		nsList = append(nsList, osdnapi.NetNamespace{Name: netns.Name, NetID: netns.NetID})
311
+	}
312
+	return nsList, nil
313
+}
314
+
315
+func (oi *OsdnRegistryInterface) GetNetNamespace(name string) (osdnapi.NetNamespace, error) {
316
+	netns, err := oi.oClient.NetNamespaces().Get(name)
317
+	if err != nil {
318
+		return osdnapi.NetNamespace{}, err
319
+	}
320
+	return osdnapi.NetNamespace{Name: netns.Name, NetID: netns.NetID}, nil
321
+}
322
+
323
+func (oi *OsdnRegistryInterface) WriteNetNamespace(name string, id uint) error {
324
+	netns := &api.NetNamespace{
325
+		TypeMeta:   kapi.TypeMeta{Kind: "NetNamespace"},
326
+		ObjectMeta: kapi.ObjectMeta{Name: name},
327
+		NetName:    name,
328
+		NetID:      id,
329
+	}
330
+	_, err := oi.oClient.NetNamespaces().Create(netns)
331
+	return err
332
+}
333
+
334
+func (oi *OsdnRegistryInterface) DeleteNetNamespace(name string) error {
335
+	return oi.oClient.NetNamespaces().Delete(name)
336
+}
... ...
@@ -614,6 +614,7 @@ _oc_get()
614 614
     must_have_one_noun+=("ispersonalsubjectaccessreview")
615 615
     must_have_one_noun+=("limitrange")
616 616
     must_have_one_noun+=("namespace")
617
+    must_have_one_noun+=("netnamespace")
617 618
     must_have_one_noun+=("node")
618 619
     must_have_one_noun+=("oauthaccesstoken")
619 620
     must_have_one_noun+=("oauthauthorizetoken")
... ...
@@ -2033,6 +2033,7 @@ _openshift_cli_get()
2033 2033
     must_have_one_noun+=("ispersonalsubjectaccessreview")
2034 2034
     must_have_one_noun+=("limitrange")
2035 2035
     must_have_one_noun+=("namespace")
2036
+    must_have_one_noun+=("netnamespace")
2036 2037
     must_have_one_noun+=("node")
2037 2038
     must_have_one_noun+=("oauthaccesstoken")
2038 2039
     must_have_one_noun+=("oauthauthorizetoken")
2039 2040
deleted file mode 100755
... ...
@@ -1,88 +0,0 @@
1
-#!/bin/bash
2
-set -ex
3
-source $(dirname $0)/provision-config.sh
4
-
5
-MINION_IP=$4
6
-MINION_ID=$5
7
-DOCKER_BRIDGE=kbr0
8
-OVS_SWITCH=obr0
9
-GRE_TUNNEL_BASE=gre
10
-BRIDGE_BASE=10.244
11
-BRIDGE_ADDRESS=${BRIDGE_BASE}.${MINION_ID}.1
12
-BRIDGE_NETWORK=${BRIDGE_ADDRESS}/24
13
-BRIDGE_NETMASK=255.255.255.0
14
-NETWORK_CONF_PATH=/etc/sysconfig/network-scripts/
15
-POST_NETWORK_SCRIPT=/vagrant/network_closure.sh
16
-
17
-# Add docker bridge ifcfg file
18
-cat <<EOF > ${NETWORK_CONF_PATH}ifcfg-${DOCKER_BRIDGE}
19
-# Generated by yours truly
20
-DEVICE=${DOCKER_BRIDGE}
21
-ONBOOT=yes
22
-TYPE=Bridge
23
-BOOTPROTO=static
24
-IPADDR=${BRIDGE_ADDRESS}
25
-NETMASK=${BRIDGE_NETMASK}
26
-STP=yes
27
-EOF
28
-
29
-# Add the ovs bridge ifcfg file
30
-cat <<EOF > ${NETWORK_CONF_PATH}ifcfg-${OVS_SWITCH}
31
-DEVICE=${OVS_SWITCH}
32
-ONBOOT=yes
33
-DEVICETYPE=ovs
34
-TYPE=OVSBridge
35
-BOOTPROTO=static
36
-HOTPLUG=no
37
-BRIDGE=${DOCKER_BRIDGE}
38
-EOF
39
-
40
-# Loop through all other minions and create persistent gre tunnels
41
-MINION_IPS=$3
42
-MINION_IP_ARRAY=(`echo ${MINION_IPS} | tr "," "\n"`)
43
-GRE_NUM=0
44
-for remote_ip in "${MINION_IP_ARRAY[@]}"
45
-do
46
-    if [ "${remote_ip}" == "${MINION_IP}" ]; then
47
-         continue
48
-    fi
49
-    ((GRE_NUM++)) || echo
50
-    GRE_TUNNEL=${GRE_TUNNEL_BASE}${GRE_NUM}
51
-    # ovs-vsctl add-port ${OVS_SWITCH} ${GRE_TUNNEL} -- set interface ${GRE_TUNNEL} type=gre options:remote_ip=${remote_ip}
52
-    cat <<EOF >  ${NETWORK_CONF_PATH}ifcfg-${GRE_TUNNEL}
53
-DEVICE=${GRE_TUNNEL}
54
-ONBOOT=yes
55
-DEVICETYPE=ovs
56
-TYPE=OVSTunnel
57
-OVS_BRIDGE=${OVS_SWITCH}
58
-OVS_TUNNEL_TYPE=gre
59
-OVS_TUNNEL_OPTIONS="options:remote_ip=${remote_ip}"
60
-EOF
61
-done
62
-
63
-# Add ip route rules such that all pod traffic flows through docker bridge and consequently to the gre tunnels
64
-cat <<EOF > /${NETWORK_CONF_PATH}route-${DOCKER_BRIDGE}
65
-${BRIDGE_BASE}.0.0/16 dev ${DOCKER_BRIDGE} scope link src ${BRIDGE_ADDRESS}
66
-EOF
67
-
68
-systemctl enable openvswitch
69
-systemctl start openvswitch
70
-
71
-# NAT interface fails to revive on network restart, so OR-gate to true
72
-systemctl restart network.service || true
73
-
74
-# Set docker bridge up, and set stp on the OVS bridge
75
-ip link set dev ${DOCKER_BRIDGE} up
76
-ovs-vsctl set Bridge ${OVS_SWITCH} stp_enable=true
77
-
78
-# Modify the docker service file such that it uses the kube docker bridge and not its own
79
-sed -ie "s/ExecStart=\/usr\/bin\/docker -d/ExecStart=\/usr\/bin\/docker -d -b=${DOCKER_BRIDGE} --iptables=false/g" /usr/lib/systemd/system/docker.service
80
-systemctl daemon-reload
81
-systemctl enable docker.service
82
-systemctl restart docker.service
83
-
84
-# Setup iptables masquerade rules, so the pods can reach the internet
85
-iptables -t nat -A POSTROUTING -s ${BRIDGE_BASE}.0.0/16 ! -d ${BRIDGE_BASE}.0.0/16 -j MASQUERADE
86
-
87
-# Persist iptables rules
88
-iptables-save >& /etc/sysconfig/iptables
89 1
deleted file mode 100755
... ...
@@ -1,21 +0,0 @@
1
-#!/bin/bash
2
-set -ex
3
-source $(dirname $0)/provision-config.sh
4
-
5
-pushd $HOME
6
-# build openshift-sdn
7
-if [ -d openshift-sdn ]; then
8
-    cd openshift-sdn
9
-    git fetch origin
10
-    git reset --hard origin/master
11
-else
12
-    git clone https://github.com/openshift/openshift-sdn
13
-    cd openshift-sdn
14
-fi
15
-
16
-make clean
17
-make
18
-make install
19
-popd
20
-
21
-# no need to start openshift-sdn, as it is integrated with openshift binary
... ...
@@ -4,6 +4,10 @@ set -ex
4 4
 source $(dirname $0)/provision-config.sh
5 5
 
6 6
 OPENSHIFT_SDN=$4
7
+if [ "${OPENSHIFT_SDN}" == "redhat/openshift-ovs-multitenant" ] || [ "${OPENSHIFT_SDN}" == "redhat/openshift-ovs-subnet" ] || [ "${OPENSHIFT_SDN}" == "" ]; then
8
+	OPENSHIFT_SDN_PLUGIN=${OPENSHIFT_SDN}
9
+fi
10
+OPENSHIFT_SDN_PLUGIN=${OPENSHIFT_SDN_PLUGIN:-redhat/openshift-ovs-subnet}
7 11
 
8 12
 NETWORK_CONF_PATH=/etc/sysconfig/network-scripts/
9 13
 sed -i 's/^NM_CONTROLLED=no/#NM_CONTROLLED=no/' ${NETWORK_CONF_PATH}ifcfg-eth1
... ...
@@ -63,7 +67,7 @@ pushd /vagrant
63 63
       --node="${minion}" \
64 64
       --hostnames="${minion},${ip}" \
65 65
       --master="https://${MASTER_IP}:8443" \
66
-      --network-plugin="redhat/openshift-ovs-subnet" \
66
+      --network-plugin="${OPENSHIFT_SDN_PLUGIN}" \
67 67
       --node-client-certificate-authority="${CERT_DIR}/ca.crt" \
68 68
       --certificate-authority="${CERT_DIR}/ca.crt" \
69 69
       --signer-cert="${CERT_DIR}/ca.crt" \
... ...
@@ -86,7 +90,7 @@ Requires=docker.service network.service
86 86
 After=network.service
87 87
 
88 88
 [Service]
89
-ExecStart=/usr/bin/openshift start master --master=https://${MASTER_IP}:8443 --nodes=${node_list} --network-plugin=redhat/openshift-ovs-subnet
89
+ExecStart=/usr/bin/openshift start master --master=https://${MASTER_IP}:8443 --nodes=${node_list} --network-plugin=${OPENSHIFT_SDN_PLUGIN}
90 90
 WorkingDirectory=/vagrant/
91 91
 
92 92
 [Install]
... ...
@@ -97,13 +101,8 @@ EOF
97 97
 systemctl daemon-reload
98 98
 systemctl start openshift-master.service
99 99
 
100
-# if SDN requires service on master, then set it up
101
-if [ "${OPENSHIFT_SDN}" != "ovs-gre" ]; then
102
-  export ETCD_CAFILE=/vagrant/openshift.local.config/master/ca.crt
103
-  export ETCD_CERTFILE=/vagrant/openshift.local.config/master/master.etcd-client.crt
104
-  export ETCD_KEYFILE=/vagrant/openshift.local.config/master/master.etcd-client.key
105
-  $(dirname $0)/provision-master-sdn.sh $@
106
-fi
100
+# setup SDN
101
+$(dirname $0)/provision-sdn.sh $@
107 102
 
108 103
 # Set up the KUBECONFIG environment variable for use by oc
109 104
 echo 'export KUBECONFIG=/vagrant/openshift.local.config/master/admin.kubeconfig' >> /root/.bash_profile
... ...
@@ -48,15 +48,10 @@ popd
48 48
 cp -r /vagrant/openshift.local.config /
49 49
 chown -R vagrant.vagrant /openshift.local.config
50 50
 
51
-if [ "${OPENSHIFT_SDN}" != "ovs-gre" ]; then
52
-  export ETCD_CAFILE=/openshift.local.config/master/ca.crt
53
-  export ETCD_CERTFILE=/openshift.local.config/master/master.etcd-client.crt
54
-  export ETCD_KEYFILE=/openshift.local.config/master/master.etcd-client.key
55
-  $(dirname $0)/provision-node-sdn.sh $@
56
-else
57
-  # Setup default networking between the nodes
58
-  $(dirname $0)/provision-gre-network.sh $@
59
-fi
51
+mkdir -p /openshift.local.volumes
52
+
53
+# Setup SDN
54
+$(dirname $0)/provision-sdn.sh $@
60 55
 
61 56
 # Create systemd service
62 57
 cat <<EOF > /usr/lib/systemd/system/openshift-node.service
63 58
deleted file mode 100755
... ...
@@ -1,25 +0,0 @@
1
-#!/bin/bash
2
-set -ex
3
-source $(dirname $0)/provision-config.sh
4
-MINION_IP=$4
5
-
6
-pushd $HOME
7
-# build openshift-sdn
8
-if [ -d openshift-sdn ]; then
9
-    cd openshift-sdn
10
-    git fetch origin
11
-    git reset --hard origin/master
12
-else
13
-    git clone https://github.com/openshift/openshift-sdn
14
-    cd openshift-sdn
15
-fi
16
-
17
-make clean
18
-make
19
-make install
20
-popd
21
-
22
-systemctl enable openvswitch
23
-systemctl start openvswitch
24
-
25
-# no need to start openshift-sdn, as it is integrated with openshift binary
26 1
new file mode 100755
... ...
@@ -0,0 +1,25 @@
0
+#!/bin/bash
1
+set -ex
2
+source $(dirname $0)/provision-config.sh
3
+
4
+pushd $HOME
5
+# build openshift-sdn
6
+if [ -d openshift-sdn ]; then
7
+    cd openshift-sdn
8
+    git fetch origin
9
+    git reset --hard origin/master
10
+    git checkout -b multitenant
11
+else
12
+    git clone https://github.com/openshift/openshift-sdn -b multitenant
13
+    cd openshift-sdn
14
+fi
15
+
16
+make clean
17
+make
18
+make install
19
+popd
20
+
21
+systemctl enable openvswitch
22
+systemctl start openvswitch
23
+
24
+# no need to start openshift-sdn, as it is integrated with openshift binary