Browse code

Admin command to manage project network

- Join project networks:
oadm pod-network join-projects --to=<project-name> <list of projects>|--selector=<project-labels>
- Unisolate project networks:
oadm pod-network unisolate-projects <list of projects>|--selector=<project-labels>
- Isolate project networks: TBD (some preliminary work done)

- Persist Net namespaces with VNID=0
- Release VNID only if no namespace is using it
- openshift network plugins can handle non pod infra container
- openshift-ovs-multitenant/openshift-ovs-subnet cleanup

Ravi Sankar Penta authored on 2015/09/29 07:38:34
Showing 13 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,78 @@
0
+package network
1
+
2
+import (
3
+	"fmt"
4
+	"io"
5
+
6
+	"github.com/spf13/cobra"
7
+
8
+	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
9
+	kerrors "k8s.io/kubernetes/pkg/util/errors"
10
+
11
+	"github.com/openshift/openshift-sdn/plugins/osdn/multitenant"
12
+	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
13
+)
14
+
15
+const (
16
+	IsolateProjectsNetworkCommandName = "isolate-projects"
17
+
18
+	isolateProjectsNetworkLong = `
19
+Isolate project network
20
+
21
+Allows projects to isolate their network from other projects when using the %[1]s network plugin.`
22
+
23
+	isolateProjectsNetworkExample = `	// Provide isolation for project p1
24
+	$ %[1]s <p1>
25
+
26
+	// Allow all projects with label name=top-secret to have their own isolated project network
27
+	$ %[1]s --selector='name=top-secret'`
28
+)
29
+
30
+type IsolateOptions struct {
31
+	Options *ProjectOptions
32
+}
33
+
34
+func NewCmdIsolateProjectsNetwork(commandName, fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
35
+	opts := &ProjectOptions{}
36
+	isolateOp := &IsolateOptions{Options: opts}
37
+
38
+	cmd := &cobra.Command{
39
+		Use:     commandName,
40
+		Short:   "Isolate project network",
41
+		Long:    fmt.Sprintf(isolateProjectsNetworkLong, multitenant.NetworkPluginName()),
42
+		Example: fmt.Sprintf(isolateProjectsNetworkExample, fullName),
43
+		Run: func(c *cobra.Command, args []string) {
44
+			if err := opts.Complete(f, c, args, out); err != nil {
45
+				kcmdutil.CheckErr(err)
46
+			}
47
+			opts.CheckSelector = c.Flag("selector").Changed
48
+			if err := opts.Validate(); err != nil {
49
+				kcmdutil.CheckErr(kcmdutil.UsageError(c, err.Error()))
50
+			}
51
+
52
+			err := isolateOp.Run()
53
+			kcmdutil.CheckErr(err)
54
+		},
55
+	}
56
+	flags := cmd.Flags()
57
+
58
+	// Common optional params
59
+	flags.StringVar(&opts.Selector, "selector", "", "Label selector to filter projects. Either pass one/more projects as arguments or use this project selector")
60
+
61
+	return cmd
62
+}
63
+
64
+func (i *IsolateOptions) Run() error {
65
+	projects, err := i.Options.GetProjects()
66
+	if err != nil {
67
+		return err
68
+	}
69
+
70
+	errList := []error{}
71
+	for _, project := range projects {
72
+		// TBD: Create or Update network namespace
73
+		// TODO: Fix this once we move VNID allocation to REST layer
74
+		errList = append(errList, fmt.Errorf("Project '%s' can not be isolated. Isolate project network feature yet to be implemented!", project.ObjectMeta.Name))
75
+	}
76
+	return kerrors.NewAggregate(errList)
77
+}
0 78
new file mode 100644
... ...
@@ -0,0 +1,100 @@
0
+package network
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+	"io"
6
+
7
+	"github.com/spf13/cobra"
8
+
9
+	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
10
+	kerrors "k8s.io/kubernetes/pkg/util/errors"
11
+
12
+	"github.com/openshift/openshift-sdn/plugins/osdn/multitenant"
13
+	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
14
+)
15
+
16
+const (
17
+	JoinProjectsNetworkCommandName = "join-projects"
18
+
19
+	joinProjectsNetworkLong = `
20
+Join project network
21
+
22
+Allows projects to join existing project network when using the %[1]s network plugin.`
23
+
24
+	joinProjectsNetworkExample = `	// Allow project p2 to use project p1 network
25
+	$ %[1]s --to=<p1> <p2>
26
+
27
+	// Allow all projects with label name=top-secret to use project p1 network
28
+	$ %[1]s --to=<p1> --selector='name=top-secret'`
29
+)
30
+
31
+type JoinOptions struct {
32
+	Options *ProjectOptions
33
+
34
+	joinProjectName string
35
+}
36
+
37
+func NewCmdJoinProjectsNetwork(commandName, fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
38
+	opts := &ProjectOptions{}
39
+	joinOp := &JoinOptions{Options: opts}
40
+
41
+	cmd := &cobra.Command{
42
+		Use:     commandName,
43
+		Short:   "Join project network",
44
+		Long:    fmt.Sprintf(joinProjectsNetworkLong, multitenant.NetworkPluginName()),
45
+		Example: fmt.Sprintf(joinProjectsNetworkExample, fullName),
46
+		Run: func(c *cobra.Command, args []string) {
47
+			if err := opts.Complete(f, c, args, out); err != nil {
48
+				kcmdutil.CheckErr(err)
49
+			}
50
+			opts.CheckSelector = c.Flag("selector").Changed
51
+			if err := joinOp.Validate(); err != nil {
52
+				kcmdutil.CheckErr(kcmdutil.UsageError(c, err.Error()))
53
+			}
54
+
55
+			err := joinOp.Run()
56
+			kcmdutil.CheckErr(err)
57
+		},
58
+	}
59
+	flags := cmd.Flags()
60
+
61
+	// Supported operations
62
+	flags.StringVar(&joinOp.joinProjectName, "to", "", "Join network of the given project name")
63
+
64
+	// Common optional params
65
+	flags.StringVar(&opts.Selector, "selector", "", "Label selector to filter projects. Either pass one/more projects as arguments or use this project selector")
66
+
67
+	return cmd
68
+}
69
+
70
+func (j *JoinOptions) Validate() error {
71
+	errList := []error{}
72
+	if err := j.Options.Validate(); err != nil {
73
+		errList = append(errList, err)
74
+	}
75
+	if len(j.joinProjectName) == 0 {
76
+		errList = append(errList, errors.New("must provide --to=<project_name>"))
77
+	}
78
+	return kerrors.NewAggregate(errList)
79
+}
80
+
81
+func (j *JoinOptions) Run() error {
82
+	netID, err := j.Options.GetNetID(j.joinProjectName)
83
+	if err != nil {
84
+		return err
85
+	}
86
+	projects, err := j.Options.GetProjects()
87
+	if err != nil {
88
+		return err
89
+	}
90
+
91
+	errList := []error{}
92
+	for _, project := range projects {
93
+		err = j.Options.CreateOrUpdateNetNamespace(project.ObjectMeta.Name, netID)
94
+		if err != nil {
95
+			errList = append(errList, fmt.Errorf("Project '%s' failed to join '%s', error: %v", project.ObjectMeta.Name, j.joinProjectName, err))
96
+		}
97
+	}
98
+	return kerrors.NewAggregate(errList)
99
+}
0 100
new file mode 100644
... ...
@@ -0,0 +1,37 @@
0
+package network
1
+
2
+import (
3
+	"io"
4
+
5
+	"github.com/spf13/cobra"
6
+
7
+	cmdutil "github.com/openshift/origin/pkg/cmd/util"
8
+	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
9
+)
10
+
11
+const PodNetworkCommandName = "pod-network"
12
+
13
+const (
14
+	podNetworkLong = `
15
+Manage pod network in the cluster
16
+
17
+This command provides common pod network operations for administrators.`
18
+)
19
+
20
+func NewCmdPodNetwork(name, fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
21
+	// Parent command to which all subcommands are added.
22
+	cmds := &cobra.Command{
23
+		Use:   name,
24
+		Short: "Manage pod network",
25
+		Long:  podNetworkLong,
26
+		Run:   cmdutil.DefaultSubCommandRun(out),
27
+	}
28
+
29
+	cmds.AddCommand(NewCmdJoinProjectsNetwork(JoinProjectsNetworkCommandName, fullName+" "+JoinProjectsNetworkCommandName, f, out))
30
+	cmds.AddCommand(NewCmdUnIsolateProjectsNetwork(UnIsolateProjectsNetworkCommandName, fullName+" "+UnIsolateProjectsNetworkCommandName, f, out))
31
+
32
+	// TODO: Enable isolate-projects subcommand once we move VNID allocation to REST layer
33
+	//cmds.AddCommand(NewCmdIsolateProjectsNetwork(IsolateProjectsNetworkCommandName, fullName+" "+IsolateProjectsNetworkCommandName, f, out))
34
+
35
+	return cmds
36
+}
0 37
new file mode 100644
... ...
@@ -0,0 +1,186 @@
0
+package network
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+	"io"
6
+	"reflect"
7
+	"strings"
8
+
9
+	"github.com/spf13/cobra"
10
+
11
+	kapi "k8s.io/kubernetes/pkg/api"
12
+	"k8s.io/kubernetes/pkg/api/meta"
13
+	kclient "k8s.io/kubernetes/pkg/client/unversioned"
14
+	"k8s.io/kubernetes/pkg/kubectl/resource"
15
+	"k8s.io/kubernetes/pkg/labels"
16
+	"k8s.io/kubernetes/pkg/runtime"
17
+	kerrors "k8s.io/kubernetes/pkg/util/errors"
18
+	"k8s.io/kubernetes/pkg/util/sets"
19
+
20
+	osclient "github.com/openshift/origin/pkg/client"
21
+	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
22
+	"github.com/openshift/origin/pkg/project/api"
23
+	sdnapi "github.com/openshift/origin/pkg/sdn/api"
24
+)
25
+
26
+type ProjectOptions struct {
27
+	DefaultNamespace string
28
+	Oclient          *osclient.Client
29
+	Kclient          *kclient.Client
30
+	Out              io.Writer
31
+
32
+	Mapper            meta.RESTMapper
33
+	Typer             runtime.ObjectTyper
34
+	RESTClientFactory func(mapping *meta.RESTMapping) (resource.RESTClient, error)
35
+
36
+	ProjectNames []string
37
+
38
+	// Common optional params
39
+	Selector      string
40
+	CheckSelector bool
41
+}
42
+
43
+func (p *ProjectOptions) Complete(f *clientcmd.Factory, c *cobra.Command, args []string, out io.Writer) error {
44
+	defaultNamespace, _, err := f.DefaultNamespace()
45
+	if err != nil {
46
+		return err
47
+	}
48
+	oc, kc, err := f.Clients()
49
+	if err != nil {
50
+		return err
51
+	}
52
+	mapper, typer := f.Object()
53
+
54
+	p.DefaultNamespace = defaultNamespace
55
+	p.Oclient = oc
56
+	p.Kclient = kc
57
+	p.Out = out
58
+	p.Mapper = mapper
59
+	p.Typer = typer
60
+	p.RESTClientFactory = f.Factory.RESTClient
61
+	p.ProjectNames = []string{}
62
+	if len(args) != 0 {
63
+		p.ProjectNames = append(p.ProjectNames, args...)
64
+	}
65
+	return nil
66
+}
67
+
68
+// Common validations
69
+func (p *ProjectOptions) Validate() error {
70
+	errList := []error{}
71
+	if p.CheckSelector {
72
+		if len(p.Selector) > 0 {
73
+			if _, err := labels.Parse(p.Selector); err != nil {
74
+				errList = append(errList, errors.New("--selector=<project_selector> must be a valid label selector"))
75
+			}
76
+		}
77
+		if len(p.ProjectNames) != 0 {
78
+			errList = append(errList, errors.New("either specify --selector=<project_selector> or projects but not both"))
79
+		}
80
+	} else if len(p.ProjectNames) == 0 {
81
+		errList = append(errList, errors.New("must provide --selector=<project_selector> or projects"))
82
+	}
83
+
84
+	// TODO: Validate if the openshift master is running with mutitenant network plugin
85
+	return kerrors.NewAggregate(errList)
86
+}
87
+
88
+func (p *ProjectOptions) GetProjects() ([]*api.Project, error) {
89
+	nameArgs := []string{"projects"}
90
+	if len(p.ProjectNames) != 0 {
91
+		nameArgs = append(nameArgs, p.ProjectNames...)
92
+	}
93
+
94
+	r := resource.NewBuilder(p.Mapper, p.Typer, resource.ClientMapperFunc(p.RESTClientFactory)).
95
+		ContinueOnError().
96
+		NamespaceParam(p.DefaultNamespace).
97
+		SelectorParam(p.Selector).
98
+		ResourceTypeOrNameArgs(true, nameArgs...).
99
+		Flatten().
100
+		Do()
101
+	if r.Err() != nil {
102
+		return nil, r.Err()
103
+	}
104
+
105
+	errList := []error{}
106
+	projectList := []*api.Project{}
107
+	_ = r.Visit(func(info *resource.Info, err error) error {
108
+		if err != nil {
109
+			return err
110
+		}
111
+		project, ok := info.Object.(*api.Project)
112
+		if !ok {
113
+			err := fmt.Errorf("cannot convert input to Project: %v", reflect.TypeOf(info.Object))
114
+			errList = append(errList, err)
115
+			// Don't bail out if one project fails
116
+			return nil
117
+		}
118
+		projectList = append(projectList, project)
119
+		return nil
120
+	})
121
+	if len(errList) != 0 {
122
+		return projectList, kerrors.NewAggregate(errList)
123
+	}
124
+
125
+	if len(projectList) == 0 {
126
+		return projectList, fmt.Errorf("No projects found")
127
+	} else {
128
+		givenProjectNames := sets.NewString(p.ProjectNames...)
129
+		foundProjectNames := sets.String{}
130
+		for _, project := range projectList {
131
+			foundProjectNames.Insert(project.ObjectMeta.Name)
132
+		}
133
+		skippedProjectNames := givenProjectNames.Difference(foundProjectNames)
134
+		if skippedProjectNames.Len() > 0 {
135
+			return projectList, fmt.Errorf("Projects %v not found", strings.Join(skippedProjectNames.List(), ", "))
136
+		}
137
+	}
138
+	return projectList, nil
139
+}
140
+
141
+func (p *ProjectOptions) GetNetNamespaces() (*sdnapi.NetNamespaceList, error) {
142
+	netNamespaces, err := p.Oclient.NetNamespaces().List()
143
+	if err != nil {
144
+		return nil, err
145
+	}
146
+	return netNamespaces, nil
147
+}
148
+
149
+func (p *ProjectOptions) GetNetID(name string) (uint, error) {
150
+	var netID uint
151
+	netNamespaces, err := p.GetNetNamespaces()
152
+	if err != nil {
153
+		return netID, err
154
+	}
155
+
156
+	for _, netNs := range netNamespaces.Items {
157
+		if name == netNs.ObjectMeta.Name {
158
+			return netNs.NetID, nil
159
+		}
160
+	}
161
+	return netID, fmt.Errorf("Net ID not found for project: %s", name)
162
+}
163
+
164
+func (p *ProjectOptions) CreateOrUpdateNetNamespace(name string, id uint) error {
165
+	netns, err := p.Oclient.NetNamespaces().Get(name)
166
+	if err != nil {
167
+		// Create netns
168
+		netns := newNetNamespace(name, id)
169
+		_, err = p.Oclient.NetNamespaces().Create(netns)
170
+	} else if netns.NetID != id {
171
+		// Update netns
172
+		netns.NetID = id
173
+		_, err = p.Oclient.NetNamespaces().Update(netns)
174
+	}
175
+	return err
176
+}
177
+
178
+func newNetNamespace(name string, id uint) *sdnapi.NetNamespace {
179
+	return &sdnapi.NetNamespace{
180
+		TypeMeta:   kapi.TypeMeta{Kind: "NetNamespace"},
181
+		ObjectMeta: kapi.ObjectMeta{Name: name},
182
+		NetName:    name,
183
+		NetID:      id,
184
+	}
185
+}
0 186
new file mode 100644
... ...
@@ -0,0 +1,80 @@
0
+package network
1
+
2
+import (
3
+	"fmt"
4
+	"io"
5
+
6
+	"github.com/spf13/cobra"
7
+
8
+	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
9
+	kerrors "k8s.io/kubernetes/pkg/util/errors"
10
+
11
+	"github.com/openshift/openshift-sdn/pkg/ovssubnet"
12
+	"github.com/openshift/openshift-sdn/plugins/osdn/multitenant"
13
+	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
14
+)
15
+
16
+const (
17
+	UnIsolateProjectsNetworkCommandName = "unisolate-projects"
18
+
19
+	unIsolateProjectsNetworkLong = `
20
+UnIsolate project network
21
+
22
+Allows projects to access all pods in the cluster and vice versa when using the %[1]s network plugin.`
23
+
24
+	unIsolateProjectsNetworkExample = `	// Allow project p1 to access all pods in the cluster and vice versa
25
+	$ %[1]s <p1>
26
+
27
+	// Allow all projects with label name=share to access all pods in the cluster and vice versa
28
+	$ %[1]s --selector='name=share'`
29
+)
30
+
31
+type UnIsolateOptions struct {
32
+	Options *ProjectOptions
33
+}
34
+
35
+func NewCmdUnIsolateProjectsNetwork(commandName, fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
36
+	opts := &ProjectOptions{}
37
+	unIsolateOp := &UnIsolateOptions{Options: opts}
38
+
39
+	cmd := &cobra.Command{
40
+		Use:     commandName,
41
+		Short:   "UnIsolate project network",
42
+		Long:    fmt.Sprintf(unIsolateProjectsNetworkLong, multitenant.NetworkPluginName()),
43
+		Example: fmt.Sprintf(unIsolateProjectsNetworkExample, fullName),
44
+		Run: func(c *cobra.Command, args []string) {
45
+			if err := opts.Complete(f, c, args, out); err != nil {
46
+				kcmdutil.CheckErr(err)
47
+			}
48
+			opts.CheckSelector = c.Flag("selector").Changed
49
+			if err := opts.Validate(); err != nil {
50
+				kcmdutil.CheckErr(kcmdutil.UsageError(c, err.Error()))
51
+			}
52
+
53
+			err := unIsolateOp.Run()
54
+			kcmdutil.CheckErr(err)
55
+		},
56
+	}
57
+	flags := cmd.Flags()
58
+
59
+	// Common optional params
60
+	flags.StringVar(&opts.Selector, "selector", "", "Label selector to filter projects. Either pass one/more projects as arguments or use this project selector")
61
+
62
+	return cmd
63
+}
64
+
65
+func (u *UnIsolateOptions) Run() error {
66
+	projects, err := u.Options.GetProjects()
67
+	if err != nil {
68
+		return err
69
+	}
70
+
71
+	errList := []error{}
72
+	for _, project := range projects {
73
+		err = u.Options.CreateOrUpdateNetNamespace(project.ObjectMeta.Name, ovssubnet.AdminVNID)
74
+		if err != nil {
75
+			errList = append(errList, fmt.Errorf("Removing network isolation for project '%s' failed, error: %v", project.ObjectMeta.Name, err))
76
+		}
77
+	}
78
+	return kerrors.NewAggregate(errList)
79
+}
... ...
@@ -32,6 +32,9 @@ type SubnetRegistry interface {
32 32
 	GetServicesNetworkCIDR() (string, error)
33 33
 	GetServices() ([]Service, string, error)
34 34
 	WatchServices(receiver chan<- *ServiceEvent, ready chan<- bool, startVersion <-chan string, stop <-chan bool) error
35
+	GetServicesForNamespace(namespace string) ([]Service, error)
36
+
37
+	GetRunningPods(nodeName, namespace string) ([]Pod, error)
35 38
 }
36 39
 
37 40
 type Subnet struct {
... ...
@@ -90,3 +93,9 @@ type ServiceEvent struct {
90 90
 	Type    EventType
91 91
 	Service Service
92 92
 }
93
+
94
+type Pod struct {
95
+	Name        string
96
+	Namespace   string
97
+	ContainerID string
98
+}
... ...
@@ -21,6 +21,8 @@ import (
21 21
 const (
22 22
 	// Maximum VXLAN Network Identifier as per RFC#7348
23 23
 	MaxVNID = ((1 << 24) - 1)
24
+	// VNID for the admin namespaces
25
+	AdminVNID = uint(0)
24 26
 )
25 27
 
26 28
 type OvsController struct {
... ...
@@ -39,10 +41,14 @@ type OvsController struct {
39 39
 
40 40
 type FlowController interface {
41 41
 	Setup(localSubnetCIDR, clusterNetworkCIDR, serviceNetworkCIDR string, mtu uint) error
42
+
42 43
 	AddOFRules(nodeIP, nodeSubnetCIDR, localIP string) error
43 44
 	DelOFRules(nodeIP, localIP string) error
45
+
44 46
 	AddServiceOFRules(netID uint, IP string, protocol api.ServiceProtocol, port uint) error
45 47
 	DelServiceOFRules(netID uint, IP string, protocol api.ServiceProtocol, port uint) error
48
+
49
+	UpdatePod(namespace, podName, containerID string, netID uint) error
46 50
 }
47 51
 
48 52
 func NewKubeController(sub api.SubnetRegistry, hostname string, selfIP string, ready chan struct{}) (*OvsController, error) {
... ...
@@ -83,6 +89,11 @@ func NewController(sub api.SubnetRegistry, hostname string, selfIP string, ready
83 83
 	}, nil
84 84
 }
85 85
 
86
+func (oc *OvsController) isMultitenant() bool {
87
+	_, is_mt := oc.flowController.(*multitenant.FlowController)
88
+	return is_mt
89
+}
90
+
86 91
 func (oc *OvsController) StartMaster(clusterNetworkCIDR string, clusterBitsPerSubnet uint, serviceNetworkCIDR string) error {
87 92
 	subrange := make([]string, 0)
88 93
 	subnets, _, err := oc.subnetRegistry.GetSubnets()
... ...
@@ -114,14 +125,16 @@ func (oc *OvsController) StartMaster(clusterNetworkCIDR string, clusterBitsPerSu
114 114
 		return err
115 115
 	}
116 116
 
117
-	if _, is_mt := oc.flowController.(*multitenant.FlowController); is_mt {
117
+	if oc.isMultitenant() {
118 118
 		nets, _, err := oc.subnetRegistry.GetNetNamespaces()
119 119
 		if err != nil {
120 120
 			return err
121 121
 		}
122 122
 		inUse := make([]uint, 0)
123 123
 		for _, net := range nets {
124
-			inUse = append(inUse, net.NetID)
124
+			if net.NetID != AdminVNID {
125
+				inUse = append(inUse, net.NetID)
126
+			}
125 127
 			oc.VNIDMap[net.Name] = net.NetID
126 128
 		}
127 129
 		// VNID: 0 reserved for default namespace and can reach any network in the cluster
... ...
@@ -135,27 +148,27 @@ func (oc *OvsController) StartMaster(clusterNetworkCIDR string, clusterBitsPerSu
135 135
 		if err != nil {
136 136
 			return err
137 137
 		}
138
+
139
+		// Handle existing namespaces
138 140
 		namespaces := result.([]string)
139
-		// Handle existing namespaces without VNID
140 141
 		for _, nsName := range namespaces {
141
-			// Skip admin namespaces, they will have VNID: 0
142
+			// Revoke invalid VNID for admin namespaces
142 143
 			if oc.isAdminNamespace(nsName) {
143
-				// Revoke VNID if already exists
144
-				if _, ok := oc.VNIDMap[nsName]; ok {
144
+				netid, ok := oc.VNIDMap[nsName]
145
+				if ok && (netid != AdminVNID) {
145 146
 					err := oc.revokeVNID(nsName)
146 147
 					if err != nil {
147 148
 						return err
148 149
 					}
149 150
 				}
150
-				continue
151
-			}
152
-			// Skip if VNID already exists for the namespace
153
-			if _, ok := oc.VNIDMap[nsName]; ok {
154
-				continue
155 151
 			}
156
-			err := oc.assignVNID(nsName)
157
-			if err != nil {
158
-				return err
152
+			_, found := oc.VNIDMap[nsName]
153
+			// Assign VNID for the namespace if it doesn't exist
154
+			if !found {
155
+				err := oc.assignVNID(nsName)
156
+				if err != nil {
157
+					return err
158
+				}
159 159
 			}
160 160
 		}
161 161
 	}
... ...
@@ -173,21 +186,28 @@ func (oc *OvsController) isAdminNamespace(nsName string) bool {
173 173
 
174 174
 func (oc *OvsController) assignVNID(namespaceName string) error {
175 175
 	_, err := oc.subnetRegistry.GetNetNamespace(namespaceName)
176
-	if err != nil {
177
-		netid, err := oc.netIDManager.GetNetID()
176
+	if err == nil {
177
+		return nil
178
+	}
179
+	var netid uint
180
+	if oc.isAdminNamespace(namespaceName) {
181
+		netid = AdminVNID
182
+	} else {
183
+		var err error
184
+		netid, err = oc.netIDManager.GetNetID()
178 185
 		if err != nil {
179 186
 			return err
180 187
 		}
181
-		err = oc.subnetRegistry.WriteNetNamespace(namespaceName, netid)
182
-		if err != nil {
183
-			e := oc.netIDManager.ReleaseNetID(netid)
184
-			if e != nil {
185
-				log.Error("Error while releasing Net ID: %v", e)
186
-			}
187
-			return err
188
+	}
189
+	err = oc.subnetRegistry.WriteNetNamespace(namespaceName, netid)
190
+	if err != nil {
191
+		e := oc.netIDManager.ReleaseNetID(netid)
192
+		if e != nil {
193
+			log.Error("Error while releasing Net ID: %v", e)
188 194
 		}
189
-		oc.VNIDMap[namespaceName] = netid
195
+		return err
190 196
 	}
197
+	oc.VNIDMap[namespaceName] = netid
191 198
 	return nil
192 199
 }
193 200
 
... ...
@@ -196,15 +216,32 @@ func (oc *OvsController) revokeVNID(namespaceName string) error {
196 196
 	if err != nil {
197 197
 		return err
198 198
 	}
199
-	netid, ok := oc.VNIDMap[namespaceName]
200
-	if !ok {
199
+	netid, found := oc.VNIDMap[namespaceName]
200
+	if !found {
201 201
 		return fmt.Errorf("Error while fetching Net ID for namespace: %s", namespaceName)
202 202
 	}
203
-	err = oc.netIDManager.ReleaseNetID(netid)
204
-	if err != nil {
205
-		return fmt.Errorf("Error while releasing Net ID: %v", err)
206
-	}
207 203
 	delete(oc.VNIDMap, namespaceName)
204
+
205
+	// Skip AdminVNID as it is not part of Net ID allocation
206
+	if netid == AdminVNID {
207
+		return nil
208
+	}
209
+
210
+	// Check if this netid is used by any other namespaces
211
+	// If not, then release the netid
212
+	netid_inuse := false
213
+	for _, id := range oc.VNIDMap {
214
+		if id == netid {
215
+			netid_inuse = true
216
+			break
217
+		}
218
+	}
219
+	if !netid_inuse {
220
+		err = oc.netIDManager.ReleaseNetID(netid)
221
+		if err != nil {
222
+			return fmt.Errorf("Error while releasing Net ID: %v", err)
223
+		}
224
+	}
208 225
 	return nil
209 226
 }
210 227
 
... ...
@@ -334,7 +371,7 @@ func (oc *OvsController) StartNode(mtu uint) error {
334 334
 	for _, s := range subnets {
335 335
 		oc.flowController.AddOFRules(s.NodeIP, s.SubnetCIDR, oc.localIP)
336 336
 	}
337
-	if _, ok := oc.flowController.(*multitenant.FlowController); ok {
337
+	if oc.isMultitenant() {
338 338
 		result, err := oc.watchAndGetResource("NetNamespace")
339 339
 		if err != nil {
340 340
 			return err
... ...
@@ -350,7 +387,11 @@ func (oc *OvsController) StartNode(mtu uint) error {
350 350
 		}
351 351
 		services := result.([]api.Service)
352 352
 		for _, svc := range services {
353
-			oc.flowController.AddServiceOFRules(oc.VNIDMap[svc.Namespace], svc.IP, svc.Protocol, svc.Port)
353
+			netid, found := oc.VNIDMap[svc.Namespace]
354
+			if !found {
355
+				return fmt.Errorf("Error fetching Net ID for namespace: %s", svc.Namespace)
356
+			}
357
+			oc.flowController.AddServiceOFRules(netid, svc.IP, svc.Protocol, svc.Port)
354 358
 		}
355 359
 	}
356 360
 
... ...
@@ -360,6 +401,31 @@ func (oc *OvsController) StartNode(mtu uint) error {
360 360
 	return nil
361 361
 }
362 362
 
363
+func (oc *OvsController) updatePodNetwork(namespace string, netID, oldNetID uint) error {
364
+	// Update OF rules for the existing/old pods in the namespace
365
+	pods, err := oc.subnetRegistry.GetRunningPods(oc.hostName, namespace)
366
+	if err != nil {
367
+		return err
368
+	}
369
+	for _, pod := range pods {
370
+		err := oc.flowController.UpdatePod(pod.Namespace, pod.Name, pod.ContainerID, netID)
371
+		if err != nil {
372
+			return err
373
+		}
374
+	}
375
+
376
+	// Update OF rules for the old services in the namespace
377
+	services, err := oc.subnetRegistry.GetServicesForNamespace(namespace)
378
+	if err != nil {
379
+		return err
380
+	}
381
+	for _, svc := range services {
382
+		oc.flowController.DelServiceOFRules(oldNetID, svc.IP, svc.Protocol, svc.Port)
383
+		oc.flowController.AddServiceOFRules(netID, svc.IP, svc.Protocol, svc.Port)
384
+	}
385
+	return nil
386
+}
387
+
363 388
 func (oc *OvsController) watchVnids(ready chan<- bool, start <-chan string) {
364 389
 	stop := make(chan bool)
365 390
 	netNsEvent := make(chan *api.NetNamespaceEvent)
... ...
@@ -367,10 +433,26 @@ func (oc *OvsController) watchVnids(ready chan<- bool, start <-chan string) {
367 367
 	for {
368 368
 		select {
369 369
 		case ev := <-netNsEvent:
370
+			oldNetID, found := oc.VNIDMap[ev.Name]
371
+			if !found {
372
+				log.Error("Error fetching Net ID for namespace: %s, skipped netNsEvent: %v", ev.Name, ev)
373
+			}
370 374
 			switch ev.Type {
371 375
 			case api.Added:
376
+				// Skip this event if the old and new network ids are same
377
+				if oldNetID == ev.NetID {
378
+					continue
379
+				}
372 380
 				oc.VNIDMap[ev.Name] = ev.NetID
381
+				err := oc.updatePodNetwork(ev.Name, ev.NetID, oldNetID)
382
+				if err != nil {
383
+					log.Error("Failed to update pod network for namespace '%s', error: %s", ev.Name, err)
384
+				}
373 385
 			case api.Deleted:
386
+				err := oc.updatePodNetwork(ev.Name, AdminVNID, oldNetID)
387
+				if err != nil {
388
+					log.Error("Failed to update pod network for namespace '%s', error: %s", ev.Name, err)
389
+				}
374 390
 				delete(oc.VNIDMap, ev.Name)
375 391
 			}
376 392
 		case <-oc.sig:
... ...
@@ -443,7 +525,10 @@ func (oc *OvsController) watchServices(ready chan<- bool, start <-chan string) {
443 443
 	for {
444 444
 		select {
445 445
 		case ev := <-svcevent:
446
-			netid := oc.VNIDMap[ev.Service.Namespace]
446
+			netid, found := oc.VNIDMap[ev.Service.Namespace]
447
+			if !found {
448
+				log.Error("Error fetching Net ID for namespace: %s, skipped serviceEvent: %v", ev.Service.Namespace, ev)
449
+			}
447 450
 			switch ev.Type {
448 451
 			case api.Added:
449 452
 				oc.flowController.AddServiceOFRules(netid, ev.Service.IP, ev.Service.Protocol, ev.Service.Port)
... ...
@@ -26,46 +26,64 @@ get_veth_host() {
26 26
     ip link show | sed -ne "s/^$veth_ifindex: \([^:@]*\).*/\1/p"
27 27
 }
28 28
 
29
-Init() {
30
-    true
31
-}
32
-
33
-Setup() {
34
-    source /etc/openshift-sdn/config.env
35
-    cluster_subnet=${OPENSHIFT_CLUSTER_SUBNET}
36
-
37
-    pid=$(docker inspect --format "{{.State.Pid}}" ${net_container})
29
+get_ipaddr_pid_veth() {
38 30
     network_mode=$(docker inspect --format "{{.HostConfig.NetworkMode}}" ${net_container})
39 31
     if [ "${network_mode}" == "host" ]; then
40 32
       # quit, nothing for the SDN here
41 33
       exit 0
34
+    elif [[ "${network_mode}" =~ container:.* ]]; then
35
+      # Get pod infra container
36
+      net_container=$(echo ${network_mode} | cut -d ":" -f 2)
42 37
     fi
43 38
     ipaddr=$(docker inspect --format "{{.NetworkSettings.IPAddress}}" ${net_container})
39
+    pid=$(docker inspect --format "{{.State.Pid}}" ${net_container})
44 40
     veth_host=$(get_veth_host $pid)
41
+}
45 42
 
43
+add_ovs_port() {
46 44
     brctl delif lbr0 $veth_host
47 45
     ovs-vsctl add-port br0 ${veth_host} 
46
+}
47
+
48
+del_ovs_port() {
49
+    ovs-vsctl del-port $veth_host
50
+}
51
+
52
+add_ovs_flows() {
48 53
     ovs_port=$(ovs-ofctl -O OpenFlow13 dump-ports-desc br0  | grep ${veth_host} | cut -d "(" -f 1 | tr -d ' ')
54
+
49 55
     ovs-ofctl -O OpenFlow13 add-flow br0 "table=0,cookie=0x${ovs_port},priority=100,ip,nw_dst=${ipaddr},actions=output:${ovs_port}"
50 56
     ovs-ofctl -O OpenFlow13 add-flow br0 "table=0,cookie=0x${ovs_port},priority=100,arp,nw_dst=${ipaddr},actions=output:${ovs_port}"
51
-
52
-    add_subnet_route="ip route add ${cluster_subnet} dev eth0 proto kernel scope link src $ipaddr"
53
-    nsenter -n -t $pid -- $add_subnet_route
54 57
 }
55 58
 
56
-Teardown() {
57
-    pid=$(docker inspect --format "{{.State.Pid}}" ${net_container})
58
-    network_mode=$(docker inspect --format "{{.HostConfig.NetworkMode}}" ${net_container})
59
-    if [ "${network_mode}" == "host" ]; then
60
-      # quit, nothing for the SDN here
61
-      exit 0
62
-    fi
63
-    veth_host=$(get_veth_host $pid)
59
+del_ovs_flows() {
64 60
     ovs_port=$(ovs-ofctl -O OpenFlow13 dump-ports-desc br0  | grep ${veth_host} | cut -d "(" -f 1 | tr -d ' ')
65
-    ovs-vsctl del-port $veth_host
61
+
66 62
     ovs-ofctl -O OpenFlow13 del-flows br0 "table=0,cookie=0x${ovs_port}/0xffffffff"
67 63
 }
68 64
 
65
+add_subnet_route() {
66
+    source /etc/openshift-sdn/config.env
67
+    local subnet_route="ip route add ${OPENSHIFT_CLUSTER_SUBNET} dev eth0 proto kernel scope link src $ipaddr"
68
+    nsenter -n -t $pid -- $subnet_route
69
+}
70
+
71
+Init() {
72
+    true
73
+}
74
+
75
+Setup() {
76
+    get_ipaddr_pid_veth
77
+    add_ovs_port
78
+    add_ovs_flows
79
+}
80
+
81
+Teardown() {
82
+    get_ipaddr_pid_veth
83
+    del_ovs_port
84
+    del_ovs_flows
85
+}
86
+
69 87
 Status() {
70 88
     # do nothing, empty output will default to address as picked by docker
71 89
     true
... ...
@@ -90,4 +108,3 @@ case "$action" in
90 90
         echo "Bad input: $@"
91 91
         exit 1
92 92
 esac
93
-
... ...
@@ -107,3 +107,7 @@ func (c *FlowController) AddServiceOFRules(netID uint, IP string, protocol api.S
107 107
 func (c *FlowController) DelServiceOFRules(netID uint, IP string, protocol api.ServiceProtocol, port uint) error {
108 108
 	return nil
109 109
 }
110
+
111
+func (c *FlowController) UpdatePod(namespace, podName, containerID string, netID uint) error {
112
+	return nil
113
+}
... ...
@@ -28,25 +28,30 @@ get_veth_host() {
28 28
     ip link show | sed -ne "s/^$veth_ifindex: \([^:@]*\).*/\1/p"
29 29
 }
30 30
 
31
-Init() {
32
-    true
33
-}
34
-
35
-Setup() {
36
-    source /etc/openshift-sdn/config.env
37
-    cluster_subnet=${OPENSHIFT_CLUSTER_SUBNET}
38
-
39
-    pid=$(docker inspect --format "{{.State.Pid}}" ${net_container})
31
+get_ipaddr_pid_veth() {
40 32
     network_mode=$(docker inspect --format "{{.HostConfig.NetworkMode}}" ${net_container})
41 33
     if [ "${network_mode}" == "host" ]; then
42 34
       # quit, nothing for the SDN here
43 35
       exit 0
36
+    elif [[ "${network_mode}" =~ container:.* ]]; then
37
+      # Get pod infra container
38
+      net_container=$(echo ${network_mode} | cut -d ":" -f 2)
44 39
     fi
45 40
     ipaddr=$(docker inspect --format "{{.NetworkSettings.IPAddress}}" ${net_container})
41
+    pid=$(docker inspect --format "{{.State.Pid}}" ${net_container})
46 42
     veth_host=$(get_veth_host $pid)
43
+}
47 44
 
45
+add_ovs_port() {
48 46
     brctl delif lbr0 $veth_host
49
-    ovs-vsctl add-port br0 ${veth_host} 
47
+    ovs-vsctl add-port br0 ${veth_host}
48
+}
49
+
50
+del_ovs_port() {
51
+    ovs-vsctl del-port $veth_host
52
+}
53
+
54
+add_ovs_flows() {
50 55
     ovs_port=$(ovs-ofctl -O OpenFlow13 dump-ports-desc br0  | grep ${veth_host} | cut -d "(" -f 1 | tr -d ' ')
51 56
 
52 57
     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"
... ...
@@ -55,25 +60,44 @@ Setup() {
55 55
     else
56 56
       ovs-ofctl -O OpenFlow13 add-flow br0 "table=6,cookie=0x${ovs_port},priority=100,ip,nw_dst=${ipaddr},reg0=${tenant_id},actions=output:${ovs_port}"
57 57
     fi
58
-
59
-    add_subnet_route="ip route add ${cluster_subnet} dev eth0 proto kernel scope link src $ipaddr"
60
-    nsenter -n -t $pid -- $add_subnet_route
61 58
 }
62 59
 
63
-Teardown() {
64
-    pid=$(docker inspect --format "{{.State.Pid}}" ${net_container})
65
-    network_mode=$(docker inspect --format "{{.HostConfig.NetworkMode}}" ${net_container})
66
-    if [ "${network_mode}" == "host" ]; then
67
-      # quit, nothing for the SDN here
68
-      exit 0
69
-    fi
70
-    veth_host=$(get_veth_host $pid)
60
+del_ovs_flows() {
71 61
     ovs_port=$(ovs-ofctl -O OpenFlow13 dump-ports-desc br0  | grep ${veth_host} | cut -d "(" -f 1 | tr -d ' ')
72
-    ovs-vsctl del-port $veth_host
62
+
73 63
     ovs-ofctl -O OpenFlow13 del-flows br0 "table=3,cookie=0x${ovs_port}/0xffffffff"
74 64
     ovs-ofctl -O OpenFlow13 del-flows br0 "table=6,cookie=0x${ovs_port}/0xffffffff"
75 65
 }
76 66
 
67
+add_subnet_route() {
68
+    source /etc/openshift-sdn/config.env
69
+    local subnet_route="ip route add ${OPENSHIFT_CLUSTER_SUBNET} dev eth0 proto kernel scope link src $ipaddr"
70
+    nsenter -n -t $pid -- $subnet_route
71
+}
72
+
73
+Init() {
74
+    true
75
+}
76
+
77
+Setup() {
78
+    get_ipaddr_pid_veth
79
+    add_ovs_port
80
+    add_ovs_flows
81
+    add_subnet_route
82
+}
83
+
84
+Update() {
85
+    get_ipaddr_pid_veth
86
+    del_ovs_flows 
87
+    add_ovs_flows
88
+}
89
+
90
+Teardown() {
91
+    get_ipaddr_pid_veth
92
+    del_ovs_port
93
+    del_ovs_flows
94
+}
95
+
77 96
 Status() {
78 97
     # do nothing, empty output will default to address as picked by docker
79 98
     true
... ...
@@ -87,6 +111,10 @@ case "$action" in
87 87
 	set -x
88 88
 	lockwrap Setup
89 89
 	;;
90
+    update)
91
+	set -x
92
+	lockwrap Update
93
+	;;
90 94
     teardown)
91 95
 	set -x
92 96
 	lockwrap Teardown
... ...
@@ -98,4 +126,3 @@ case "$action" in
98 98
         echo "Bad input: $@"
99 99
         exit 1
100 100
 esac
101
-
... ...
@@ -97,3 +97,9 @@ func generateServiceRule(netID uint, IP string, protocol api.ServiceProtocol, po
97 97
 		return fmt.Sprintf("table=4,priority=200,reg0=%d,%s,nw_dst=%s,tp_dst=%d,actions=output:2", netID, strings.ToLower(string(protocol)), IP, port)
98 98
 	}
99 99
 }
100
+
101
+func (c *FlowController) UpdatePod(namespace, podName, containerID string, netID uint) error {
102
+	out, err := exec.Command("openshift-ovs-multitenant", "update", namespace, podName, containerID, fmt.Sprint(netID)).CombinedOutput()
103
+	log.V(5).Infof("UpdatePod output: %s, error: %v", out, err)
104
+	return err
105
+}
... ...
@@ -1,6 +1,7 @@
1 1
 package multitenant
2 2
 
3 3
 import (
4
+	"fmt"
4 5
 	"strconv"
5 6
 
6 7
 	"github.com/golang/glog"
... ...
@@ -31,16 +32,6 @@ func (plugin *MultitenantPlugin) getExecutable() string {
31 31
 	return "openshift-ovs-multitenant"
32 32
 }
33 33
 
34
-func (plugin *MultitenantPlugin) getVnid(namespace string) (uint, error) {
35
-	// get vnid for the namespace
36
-	vnid, ok := plugin.OvsController.VNIDMap[namespace]
37
-	if !ok {
38
-		// vnid does not exist for this pod, set it to zero (or error?)
39
-		vnid = 0
40
-	}
41
-	return vnid, nil
42
-}
43
-
44 34
 func (plugin *MultitenantPlugin) Init(host knetwork.Host) error {
45 35
 	plugin.host = host
46 36
 	return nil
... ...
@@ -51,9 +42,9 @@ func (plugin *MultitenantPlugin) Name() string {
51 51
 }
52 52
 
53 53
 func (plugin *MultitenantPlugin) SetUpPod(namespace string, name string, id kubeletTypes.DockerID) error {
54
-	vnid, err := plugin.getVnid(namespace)
55
-	if err != nil {
56
-		return err
54
+	vnid, found := plugin.OvsController.VNIDMap[namespace]
55
+	if !found {
56
+		return fmt.Errorf("Error fetching VNID for namespace: %s", namespace)
57 57
 	}
58 58
 	out, err := utilexec.New().Command(plugin.getExecutable(), setUpCmd, namespace, name, string(id), strconv.FormatUint(uint64(vnid), 10)).CombinedOutput()
59 59
 	glog.V(5).Infof("SetUpPod 'multitenant' network plugin output: %s, %v", string(out), err)
... ...
@@ -61,7 +52,10 @@ func (plugin *MultitenantPlugin) SetUpPod(namespace string, name string, id kube
61 61
 }
62 62
 
63 63
 func (plugin *MultitenantPlugin) TearDownPod(namespace string, name string, id kubeletTypes.DockerID) error {
64
-	vnid, err := plugin.getVnid(namespace)
64
+	vnid, found := plugin.OvsController.VNIDMap[namespace]
65
+	if !found {
66
+		return fmt.Errorf("Error fetching VNID for namespace: %s", namespace)
67
+	}
65 68
 	out, err := utilexec.New().Command(plugin.getExecutable(), tearDownCmd, namespace, name, string(id), strconv.FormatUint(uint64(vnid), 10)).CombinedOutput()
66 69
 	glog.V(5).Infof("TearDownPod 'multitenant' network plugin output: %s, %v", string(out), err)
67 70
 	return err
... ...
@@ -92,6 +92,29 @@ func (oi *OsdnRegistryInterface) WatchSubnets(receiver chan<- *osdnapi.SubnetEve
92 92
 	}
93 93
 }
94 94
 
95
+func (oi *OsdnRegistryInterface) GetRunningPods(nodeName, namespace string) ([]osdnapi.Pod, error) {
96
+	fieldSelector := fields.Set{"spec.host": nodeName}.AsSelector()
97
+	podList, err := oi.kClient.Pods(namespace).List(labels.Everything(), fieldSelector)
98
+	if err != nil {
99
+		return nil, err
100
+	}
101
+
102
+	// Filter running pods and convert kapi.Pod to osdnapi.Pod
103
+	pods := make([]osdnapi.Pod, 0, len(podList.Items))
104
+	for _, pod := range podList.Items {
105
+		if pod.Status.Phase != kapi.PodRunning {
106
+			continue
107
+		}
108
+		containerID := ""
109
+		if len(pod.Status.ContainerStatuses) > 0 {
110
+			// Extract only container ID, pod.Status.ContainerStatuses[0].ContainerID is of the format: docker://<containerID>
111
+			containerID = strings.Split(pod.Status.ContainerStatuses[0].ContainerID, "://")[1]
112
+		}
113
+		pods = append(pods, osdnapi.Pod{Name: pod.ObjectMeta.Name, Namespace: pod.ObjectMeta.Namespace, ContainerID: containerID})
114
+	}
115
+	return pods, nil
116
+}
117
+
95 118
 func (oi *OsdnRegistryInterface) GetNodes() ([]osdnapi.Node, string, error) {
96 119
 	knodes, err := oi.kClient.Nodes().List(labels.Everything(), fields.Everything())
97 120
 	if err != nil {
... ...
@@ -255,12 +278,10 @@ func (oi *OsdnRegistryInterface) WatchNetNamespaces(receiver chan<- *osdnapi.Net
255 255
 		netns := obj.(*api.NetNamespace)
256 256
 
257 257
 		switch eventType {
258
-		case watch.Added:
258
+		case watch.Added, watch.Modified:
259 259
 			receiver <- &osdnapi.NetNamespaceEvent{Type: osdnapi.Added, Name: netns.NetName, NetID: netns.NetID}
260 260
 		case watch.Deleted:
261 261
 			receiver <- &osdnapi.NetNamespaceEvent{Type: osdnapi.Deleted, Name: netns.NetName}
262
-		case watch.Modified:
263
-			// Ignore, we don't need to update SDN in case of network namespace updates
264 262
 		}
265 263
 	}
266 264
 }
... ...
@@ -301,13 +322,22 @@ func (oi *OsdnRegistryInterface) DeleteNetNamespace(name string) error {
301 301
 	return oi.oClient.NetNamespaces().Delete(name)
302 302
 }
303 303
 
304
+func (oi *OsdnRegistryInterface) GetServicesForNamespace(namespace string) ([]osdnapi.Service, error) {
305
+	services, _, err := oi.getServices(namespace)
306
+	return services, err
307
+}
308
+
304 309
 func (oi *OsdnRegistryInterface) GetServices() ([]osdnapi.Service, string, error) {
305
-	kServList, err := oi.kClient.Services(kapi.NamespaceAll).List(labels.Everything())
310
+	return oi.getServices(kapi.NamespaceAll)
311
+}
312
+
313
+func (oi *OsdnRegistryInterface) getServices(namespace string) ([]osdnapi.Service, string, error) {
314
+	kServList, err := oi.kClient.Services(namespace).List(labels.Everything())
306 315
 	if err != nil {
307 316
 		return nil, "", err
308 317
 	}
309 318
 
310
-	oServList := make([]osdnapi.Service, 0)
319
+	oServList := make([]osdnapi.Service, 0, len(kServList.Items))
311 320
 	for _, kService := range kServList.Items {
312 321
 		if kService.Spec.ClusterIP == "None" {
313 322
 			continue