@@ -20,6 +20,8 @@ import (
20
20
"context"
21
21
"fmt"
22
22
sysos "os"
23
+ "slices"
24
+ "strings"
23
25
24
26
"github.com/gophercloud/gophercloud"
25
27
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
@@ -33,9 +35,9 @@ import (
33
35
34
36
// InstancesV2 encapsulates an implementation of InstancesV2 for OpenStack.
35
37
type InstancesV2 struct {
36
- compute * gophercloud.ServiceClient
37
- network * gophercloud.ServiceClient
38
- region string
38
+ compute map [ string ] * gophercloud.ServiceClient
39
+ network map [ string ] * gophercloud.ServiceClient
40
+ regions [] string
39
41
regionProviderID bool
40
42
networkingOpts NetworkingOpts
41
43
}
@@ -51,16 +53,25 @@ func (os *OpenStack) InstancesV2() (cloudprovider.InstancesV2, bool) {
51
53
func (os * OpenStack ) instancesv2 () (* InstancesV2 , bool ) {
52
54
klog .V (4 ).Info ("openstack.Instancesv2() called" )
53
55
54
- compute , err := client .NewComputeV2 (os .provider , os .epOpts )
55
- if err != nil {
56
- klog .Errorf ("unable to access compute v2 API : %v" , err )
57
- return nil , false
58
- }
56
+ var err error
57
+ compute := make (map [string ]* gophercloud.ServiceClient , len (os .regions ))
58
+ network := make (map [string ]* gophercloud.ServiceClient , len (os .regions ))
59
59
60
- network , err := client .NewNetworkV2 (os .provider , os .epOpts )
61
- if err != nil {
62
- klog .Errorf ("unable to access network v2 API : %v" , err )
63
- return nil , false
60
+ for _ , region := range os .regions {
61
+ opt := os .epOpts
62
+ opt .Region = region
63
+
64
+ compute [region ], err = client .NewComputeV2 (os .provider , opt )
65
+ if err != nil {
66
+ klog .Errorf ("unable to access compute v2 API : %v" , err )
67
+ return nil , false
68
+ }
69
+
70
+ network [region ], err = client .NewNetworkV2 (os .provider , opt )
71
+ if err != nil {
72
+ klog .Errorf ("unable to access network v2 API : %v" , err )
73
+ return nil , false
74
+ }
64
75
}
65
76
66
77
regionalProviderID := false
@@ -71,17 +82,23 @@ func (os *OpenStack) instancesv2() (*InstancesV2, bool) {
71
82
return & InstancesV2 {
72
83
compute : compute ,
73
84
network : network ,
74
- region : os .epOpts . Region ,
85
+ regions : os .regions ,
75
86
regionProviderID : regionalProviderID ,
76
87
networkingOpts : os .networkingOpts ,
77
88
}, true
78
89
}
79
90
80
91
// InstanceExists indicates whether a given node exists according to the cloud provider
81
92
func (i * InstancesV2 ) InstanceExists (ctx context.Context , node * v1.Node ) (bool , error ) {
82
- _ , err := i .getInstance (ctx , node )
93
+ klog .V (4 ).InfoS ("openstack.InstanceExists() called" , "node" , klog .KObj (node ),
94
+ "providerID" , node .Spec .ProviderID ,
95
+ "region" , node .Labels [v1 .LabelTopologyRegion ])
96
+
97
+ _ , _ , err := i .getInstance (ctx , node )
83
98
if err == cloudprovider .InstanceNotFound {
84
- klog .V (6 ).Infof ("instance not found for node: %s" , node .Name )
99
+ klog .V (6 ).InfoS ("Node is not found in cloud provider" , "node" , klog .KObj (node ),
100
+ "providerID" , node .Spec .ProviderID ,
101
+ "region" , node .Labels [v1 .LabelTopologyRegion ])
85
102
return false , nil
86
103
}
87
104
@@ -94,7 +111,11 @@ func (i *InstancesV2) InstanceExists(ctx context.Context, node *v1.Node) (bool,
94
111
95
112
// InstanceShutdown returns true if the instance is shutdown according to the cloud provider.
96
113
func (i * InstancesV2 ) InstanceShutdown (ctx context.Context , node * v1.Node ) (bool , error ) {
97
- server , err := i .getInstance (ctx , node )
114
+ klog .V (4 ).InfoS ("openstack.InstanceShutdown() called" , "node" , klog .KObj (node ),
115
+ "providerID" , node .Spec .ProviderID ,
116
+ "region" , node .Labels [v1 .LabelTopologyRegion ])
117
+
118
+ server , _ , err := i .getInstance (ctx , node )
98
119
if err != nil {
99
120
return false , err
100
121
}
@@ -109,7 +130,7 @@ func (i *InstancesV2) InstanceShutdown(ctx context.Context, node *v1.Node) (bool
109
130
110
131
// InstanceMetadata returns the instance's metadata.
111
132
func (i * InstancesV2 ) InstanceMetadata (ctx context.Context , node * v1.Node ) (* cloudprovider.InstanceMetadata , error ) {
112
- srv , err := i .getInstance (ctx , node )
133
+ srv , region , err := i .getInstance (ctx , node )
113
134
if err != nil {
114
135
return nil , err
115
136
}
@@ -118,79 +139,124 @@ func (i *InstancesV2) InstanceMetadata(ctx context.Context, node *v1.Node) (*clo
118
139
server = * srv
119
140
}
120
141
121
- instanceType , err := srvInstanceType (i .compute , & server .Server )
142
+ instanceType , err := srvInstanceType (i .compute [ region ] , & server .Server )
122
143
if err != nil {
123
144
return nil , err
124
145
}
125
146
126
- ports , err := getAttachedPorts (i .network , server .ID )
147
+ ports , err := getAttachedPorts (i .network [ region ] , server .ID )
127
148
if err != nil {
128
149
return nil , err
129
150
}
130
151
131
- addresses , err := nodeAddresses (& server .Server , ports , i .network , i .networkingOpts )
152
+ addresses , err := nodeAddresses (& server .Server , ports , i .network [ region ] , i .networkingOpts )
132
153
if err != nil {
133
154
return nil , err
134
155
}
135
156
136
157
return & cloudprovider.InstanceMetadata {
137
- ProviderID : i .makeInstanceID (& server .Server ),
158
+ ProviderID : i .makeInstanceID (& server .Server , region ),
138
159
InstanceType : instanceType ,
139
160
NodeAddresses : addresses ,
140
161
Zone : server .AvailabilityZone ,
141
- Region : i . region ,
162
+ Region : region ,
142
163
}, nil
143
164
}
144
165
145
- func (i * InstancesV2 ) makeInstanceID (srv * servers.Server ) string {
166
+ func (i * InstancesV2 ) makeInstanceID (srv * servers.Server , region string ) string {
146
167
if i .regionProviderID {
147
- return fmt .Sprintf ("%s://%s/%s" , ProviderName , i . region , srv .ID )
168
+ return fmt .Sprintf ("%s://%s/%s" , ProviderName , region , srv .ID )
148
169
}
149
170
return fmt .Sprintf ("%s:///%s" , ProviderName , srv .ID )
150
171
}
151
172
152
- func (i * InstancesV2 ) getInstance (ctx context.Context , node * v1.Node ) (* ServerAttributesExt , error ) {
153
- if node .Spec .ProviderID == "" {
154
- opt := servers.ListOpts {
155
- Name : fmt .Sprintf ("^%s$" , node .Name ),
173
+ func (i * InstancesV2 ) getInstance (ctx context.Context , node * v1.Node ) (* ServerAttributesExt , string , error ) {
174
+ klog .V (4 ).InfoS ("openstack.getInstance() called" , "node" , klog .KObj (node ),
175
+ "providerID" , node .Spec .ProviderID ,
176
+ "region" , node .Labels [v1 .LabelTopologyRegion ])
177
+
178
+ instanceID , instanceRegion , err := instanceIDFromProviderID (node .Spec .ProviderID )
179
+ if err == nil && instanceRegion != "" {
180
+ if slices .Contains (i .regions , instanceRegion ) {
181
+ return i .getInstanceByID (instanceID , []string {instanceRegion })
182
+ }
183
+
184
+ return nil , "" , fmt .Errorf ("getInstance: ProviderID \" %s\" didn't match supported regions \" %s\" " , node .Spec .ProviderID , strings .Join (i .regions , "," ))
185
+ }
186
+
187
+ // At this point we know that ProviderID is not properly set or it doesn't contain region information
188
+ // We need to search for the instance in all regions
189
+ var searchRegions []string
190
+
191
+ // We cannot trust the region label, so we need to check the region
192
+ instanceRegion = node .Labels [v1 .LabelTopologyRegion ]
193
+ if slices .Contains (i .regions , instanceRegion ) {
194
+ searchRegions = []string {instanceRegion }
195
+ }
196
+
197
+ for _ , r := range i .regions {
198
+ if r != instanceRegion {
199
+ searchRegions = append (searchRegions , r )
156
200
}
157
- mc := metrics .NewMetricContext ("server" , "list" )
158
- allPages , err := servers .List (i .compute , opt ).AllPages ()
201
+ }
202
+
203
+ klog .V (6 ).InfoS ("openstack.getInstance() trying to find the instance in regions" , "node" , klog .KObj (node ),
204
+ "instanceID" , instanceID ,
205
+ "regions" , strings .Join (searchRegions , "," ))
206
+
207
+ if instanceID == "" {
208
+ return i .getInstanceByName (node , searchRegions )
209
+ }
210
+
211
+ return i .getInstanceByID (instanceID , searchRegions )
212
+ }
213
+
214
+ func (i * InstancesV2 ) getInstanceByID (instanceID string , searchRegions []string ) (* ServerAttributesExt , string , error ) {
215
+ server := ServerAttributesExt {}
216
+
217
+ mc := metrics .NewMetricContext ("server" , "get" )
218
+ for _ , r := range searchRegions {
219
+ err := servers .Get (i .compute [r ], instanceID ).ExtractInto (& server )
159
220
if mc .ObserveRequest (err ) != nil {
160
- return nil , fmt .Errorf ("error listing servers %v: %v" , opt , err )
221
+ if errors .IsNotFound (err ) {
222
+ continue
223
+ }
224
+
225
+ return nil , "" , err
226
+ }
227
+
228
+ return & server , r , nil
229
+ }
230
+
231
+ return nil , "" , cloudprovider .InstanceNotFound
232
+ }
233
+
234
+ func (i * InstancesV2 ) getInstanceByName (node * v1.Node , searchRegions []string ) (* ServerAttributesExt , string , error ) {
235
+ opt := servers.ListOpts {
236
+ Name : fmt .Sprintf ("^%s$" , node .Name ),
237
+ }
238
+ mc := metrics .NewMetricContext ("server" , "list" )
239
+ serverList := []ServerAttributesExt {}
240
+
241
+ for _ , r := range searchRegions {
242
+ allPages , err := servers .List (i .compute [r ], opt ).AllPages ()
243
+ if mc .ObserveRequest (err ) != nil {
244
+ return nil , "" , fmt .Errorf ("error listing servers %v: %v" , opt , err )
161
245
}
162
246
163
- serverList := []ServerAttributesExt {}
164
247
err = servers .ExtractServersInto (allPages , & serverList )
165
248
if err != nil {
166
- return nil , fmt .Errorf ("error extracting servers from pages: %v" , err )
249
+ return nil , "" , fmt .Errorf ("error extracting servers from pages: %v" , err )
167
250
}
168
251
if len (serverList ) == 0 {
169
- return nil , cloudprovider . InstanceNotFound
252
+ continue
170
253
}
171
254
if len (serverList ) > 1 {
172
- return nil , fmt .Errorf ("getInstance : multiple instances found" )
255
+ return nil , "" , fmt .Errorf ("getInstanceByName : multiple instances found" )
173
256
}
174
- return & serverList [0 ], nil
175
- }
176
-
177
- instanceID , instanceRegion , err := instanceIDFromProviderID (node .Spec .ProviderID )
178
- if err != nil {
179
- return nil , err
180
- }
181
257
182
- if instanceRegion != "" && instanceRegion != i .region {
183
- return nil , fmt .Errorf ("ProviderID \" %s\" didn't match supported region \" %s\" " , node .Spec .ProviderID , i .region )
258
+ return & serverList [0 ], r , nil
184
259
}
185
260
186
- server := ServerAttributesExt {}
187
- mc := metrics .NewMetricContext ("server" , "get" )
188
- err = servers .Get (i .compute , instanceID ).ExtractInto (& server )
189
- if mc .ObserveRequest (err ) != nil {
190
- if errors .IsNotFound (err ) {
191
- return nil , cloudprovider .InstanceNotFound
192
- }
193
- return nil , err
194
- }
195
- return & server , nil
261
+ return nil , "" , cloudprovider .InstanceNotFound
196
262
}
0 commit comments