- 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
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 |