@@ -37,6 +37,8 @@ import (
3737 "github.com/vmware/govmomi/property"
3838 "github.com/vmware/govmomi/session"
3939 "github.com/vmware/govmomi/sts"
40+ "github.com/vmware/govmomi/vapi/rest"
41+ "github.com/vmware/govmomi/vapi/tags"
4042 "github.com/vmware/govmomi/vim25"
4143 "github.com/vmware/govmomi/vim25/mo"
4244 "github.com/vmware/govmomi/vim25/soap"
@@ -68,6 +70,8 @@ type VirtualCenter struct {
6870 Config * VirtualCenterConfig
6971 // Client represents the govmomi client instance for the connection.
7072 Client * govmomi.Client
73+ // RestClient represents the govmomi rest client
74+ RestClient * rest.Client
7175 // PbmClient represents the govmomi PBM Client instance.
7276 PbmClient * pbm.Client
7377 // CnsClient represents the CNS client instance.
@@ -76,6 +80,9 @@ type VirtualCenter struct {
7680 VsanClient * vsan.Client
7781 // VslmClient represents the Vslm client instance.
7882 VslmClient * vslm.Client
83+ // tagManager represents the tagmanager client instance.
84+ tagManager * tags.Manager
85+
7986 // ClientMutex is used for exclusive connection creation.
8087 ClientMutex * sync.Mutex
8188}
@@ -151,7 +158,7 @@ type VirtualCenterConfig struct {
151158}
152159
153160// NewClient creates a new govmomi Client instance.
154- func (vc * VirtualCenter ) NewClient (ctx context.Context , useragent string ) (* govmomi.Client , error ) {
161+ func (vc * VirtualCenter ) NewClient (ctx context.Context , useragent string ) (* govmomi.Client , * rest. Client , error ) {
155162 log := logger .GetLogger (ctx )
156163 if vc .Config .Scheme == "" {
157164 vc .Config .Scheme = DefaultScheme
@@ -160,14 +167,14 @@ func (vc *VirtualCenter) NewClient(ctx context.Context, useragent string) (*govm
160167 url , err := soap .ParseURL (net .JoinHostPort (vc .Config .Host , strconv .Itoa (vc .Config .Port )))
161168 if err != nil {
162169 log .Errorf ("failed to parse URL %s with err: %v" , url , err )
163- return nil , err
170+ return nil , nil , err
164171 }
165172
166173 soapClient := soap .NewClient (url , vc .Config .Insecure )
167174 if len (vc .Config .CAFile ) > 0 && ! vc .Config .Insecure {
168175 if err := soapClient .SetRootCAs (vc .Config .CAFile ); err != nil {
169176 log .Errorf ("failed to load CA file: %v" , err )
170- return nil , err
177+ return nil , nil , err
171178 }
172179 } else if len (vc .Config .Thumbprint ) > 0 && ! vc .Config .Insecure {
173180 soapClient .SetThumbprint (url .Host , vc .Config .Thumbprint )
@@ -179,36 +186,38 @@ func (vc *VirtualCenter) NewClient(ctx context.Context, useragent string) (*govm
179186 vimClient , err := vim25 .NewClient (ctx , soapClient )
180187 if err != nil {
181188 log .Errorf ("failed to create new client with err: %v" , err )
182- return nil , err
189+ return nil , nil , err
183190 }
184191 err = vimClient .UseServiceVersion ("vsan" )
185192 if err != nil && vc .Config .Host != "127.0.0.1" {
186193 // Skipping error for simulator connection for unit tests.
187194 log .Errorf ("Failed to set vimClient service version to vsan. err: %v" , err )
188- return nil , err
195+ return nil , nil , err
189196 }
190197 vimClient .UserAgent = useragent
191198 client := & govmomi.Client {
192199 Client : vimClient ,
193200 SessionManager : session .NewManager (vimClient ),
194201 }
195202
196- err = vc .login (ctx , client )
203+ restClient := rest .NewClient (client .Client )
204+
205+ err = vc .login (ctx , client , restClient )
197206 if err != nil {
198207 log .Errorf ("failed to login to vc. err: %v" , err )
199- return nil , err
208+ return nil , nil , err
200209 }
201210
202211 s , err := client .SessionManager .UserSession (ctx )
203212 if err != nil {
204213 log .Errorf ("failed to get UserSession. err: %v" , err )
205- return nil , err
214+ return nil , nil , err
206215 }
207216 // Refer to this issue - https://github.com/vmware/govmomi/issues/2922
208217 // When Session Manager -> UserSession can return nil user session with nil error
209218 // so handling the case for nil session.
210219 if s == nil {
211- return nil , errors .New ("nil session obtained from session manager" )
220+ return nil , nil , errors .New ("nil session obtained from session manager" )
212221 }
213222 log .Infof ("New session ID for '%s' = %s" , s .UserName , s .Key )
214223
@@ -217,19 +226,22 @@ func (vc *VirtualCenter) NewClient(ctx context.Context, useragent string) (*govm
217226 }
218227 rt := vim25 .Retry (client .RoundTripper , vim25 .TemporaryNetworkError (vc .Config .RoundTripperCount ))
219228 client .RoundTripper = & MetricRoundTripper {"soap" , rt }
220- return client , nil
229+ return client , restClient , nil
221230}
222231
223232// login calls SessionManager.LoginByToken if certificate and private key are
224233// configured. Otherwise, calls SessionManager.Login with user and password.
225- func (vc * VirtualCenter ) login (ctx context.Context , client * govmomi.Client ) error {
234+ func (vc * VirtualCenter ) login (ctx context.Context , client * govmomi.Client , restClient * rest. Client ) error {
226235 log := logger .GetLogger (ctx )
227236 var err error
228237
229238 b , _ := pem .Decode ([]byte (vc .Config .Username ))
230239 if b == nil {
231- return client .SessionManager .Login (ctx ,
232- neturl .UserPassword (vc .Config .Username , vc .Config .Password ))
240+ if err := client .SessionManager .Login (ctx , neturl .UserPassword (vc .Config .Username , vc .Config .Password )); err != nil {
241+ log .Errorf ("error logging soap client: %v" , err )
242+ return err
243+ }
244+ return restClient .Login (ctx , neturl .UserPassword (vc .Config .Username , vc .Config .Password ))
233245 }
234246
235247 cert , err := tls .X509KeyPair ([]byte (vc .Config .Username ), []byte (vc .Config .Password ))
@@ -246,6 +258,7 @@ func (vc *VirtualCenter) login(ctx context.Context, client *govmomi.Client) erro
246258
247259 req := sts.TokenRequest {
248260 Certificate : & cert ,
261+ Delegatable : true ,
249262 }
250263
251264 signer , err := tokens .Issue (ctx , req )
@@ -255,7 +268,11 @@ func (vc *VirtualCenter) login(ctx context.Context, client *govmomi.Client) erro
255268 }
256269
257270 header := soap.Header {Security : signer }
258- return client .SessionManager .LoginByToken (client .Client .WithHeader (ctx , header ))
271+ if err := client .SessionManager .LoginByToken (client .Client .WithHeader (ctx , header )); err != nil {
272+ return err
273+ }
274+
275+ return restClient .LoginByToken (restClient .WithSigner (ctx , signer ))
259276}
260277
261278// Connect establishes a new connection with vSphere with updated credentials.
@@ -279,6 +296,14 @@ func (vc *VirtualCenter) Connect(ctx context.Context) error {
279296 log .Errorf ("Could not logout of VC session. Error: %v" , logoutErr )
280297 }
281298 }
299+ if vc .RestClient != nil {
300+ logoutErr := vc .RestClient .Logout (ctx )
301+ if logoutErr != nil {
302+ // TODO: On vSphere U3, with a shared session this may return an error as logging
303+ // out from Soap also logs out from rest.
304+ log .Errorf ("Could not logout of VC rest session. Error: %v" , logoutErr )
305+ }
306+ }
282307 }()
283308 }
284309 return err
@@ -295,21 +320,22 @@ func (vc *VirtualCenter) connect(ctx context.Context) error {
295320 log .Errorf ("failed to get useragent for vCenter session. error: %+v" , err )
296321 return err
297322 }
298- if vc .Client == nil {
323+ if vc .Client == nil || vc . RestClient == nil {
299324 if vc .Config .ReloadVCConfigForNewClient {
300325 err = ReadVCConfigs (ctx , vc )
301326 if err != nil {
302327 return err
303328 }
304329 }
305330 log .Infof ("VirtualCenter.connect() creating new client" )
306- if vc .Client , err = vc .NewClient (ctx , useragent ); err != nil {
331+ if vc .Client , vc . RestClient , err = vc .NewClient (ctx , useragent ); err != nil {
307332 log .Errorf ("failed to create govmomi client with err: %v" , err )
308333 if ! vc .Config .Insecure {
309334 log .Errorf ("failed to connect to vCenter using CA file: %q" , vc .Config .CAFile )
310335 }
311336 return err
312337 }
338+
313339 log .Infof ("VirtualCenter.connect() successfully created new client" )
314340 return nil
315341 }
@@ -319,10 +345,25 @@ func (vc *VirtualCenter) connect(ctx context.Context) error {
319345 // SessionMgr.UserSession(ctx) retrieves and returns the SessionManager's
320346 // CurrentSession field. Nil is returned if the session is not
321347 // authenticated or timed out.
322- if userSession , err := sessionMgr .UserSession (ctx ); err != nil {
348+ shouldLogin := false
349+
350+ userSession , err := sessionMgr .UserSession (ctx )
351+ if err != nil {
323352 log .Errorf ("failed to obtain user session with err: %v" , err )
324- return err
325- } else if userSession != nil {
353+ // Login again if session is invalid
354+ shouldLogin = true
355+ //return err <- Check if we really want to return error here or follow with re-login
356+ }
357+
358+ restSession , err := vc .RestClient .Session (ctx )
359+ if err != nil {
360+ log .Errorf ("failed to obtain rest user session with err: %v" , err )
361+ // Login again if session is invalid
362+ shouldLogin = true
363+ }
364+
365+ // No need to re-login
366+ if userSession != nil && restSession != nil && ! shouldLogin {
326367 return nil
327368 }
328369
@@ -335,6 +376,14 @@ func (vc *VirtualCenter) connect(ctx context.Context) error {
335376 }
336377 }
337378
379+ if vc .RestClient != nil {
380+ // TODO: On U3 shared session this may return an error, if the Soap logout
381+ // happened correctly, but can be safely ignored
382+ if err := vc .RestClient .Logout (ctx ); err != nil {
383+ log .Errorf ("failed to logout current rest session. still clearing idle sessions. err: %v" , err )
384+ }
385+ }
386+
338387 // If session has expired, create a new instance.
339388 log .Infof ("Creating a new client session as the existing one isn't valid or not authenticated" )
340389 if vc .Config .ReloadVCConfigForNewClient {
@@ -343,7 +392,7 @@ func (vc *VirtualCenter) connect(ctx context.Context) error {
343392 return err
344393 }
345394 }
346- if vc .Client , err = vc .NewClient (ctx , useragent ); err != nil {
395+ if vc .Client , vc . RestClient , err = vc .NewClient (ctx , useragent ); err != nil {
347396 log .Errorf ("failed to create govmomi client with err: %v" , err )
348397 if ! vc .Config .Insecure {
349398 log .Errorf ("failed to connect to vCenter using CA file: %q" , vc .Config .CAFile )
@@ -382,6 +431,12 @@ func (vc *VirtualCenter) connect(ctx context.Context) error {
382431 }
383432 vc .VsanClient .RoundTripper = & MetricRoundTripper {"vsan" , vc .VsanClient .RoundTripper }
384433 }
434+
435+ // Recreate the Tag Manager client if created using timed out Rest client
436+ if vc .tagManager != nil {
437+ vc .tagManager = tags .NewManager (vc .RestClient )
438+ }
439+
385440 return nil
386441}
387442
@@ -485,7 +540,14 @@ func (vc *VirtualCenter) Disconnect(ctx context.Context) error {
485540 log .Errorf ("failed to logout with err: %v" , err )
486541 return err
487542 }
543+
544+ // We don't return an error here because logging out from Rest failing
545+ // can happen on VC shared sessions
546+ if err := vc .RestClient .Logout (ctx ); err != nil {
547+ log .Errorf ("failed to logout rest with err: %v" , err )
548+ }
488549 vc .Client = nil
550+ vc .RestClient = nil
489551 return nil
490552}
491553
0 commit comments