github.com/cilium/cilium@v1.16.2/api/v1/operator/server/server.go (about)

     1  // Code generated by go-swagger; DO NOT EDIT.
     2  
     3  // Copyright Authors of Cilium
     4  // SPDX-License-Identifier: Apache-2.0
     5  
     6  package server
     7  
     8  import (
     9  	"context"
    10  	"crypto/tls"
    11  	"crypto/x509"
    12  	"errors"
    13  	"fmt"
    14  	"log"
    15  	"net"
    16  	"net/http"
    17  	"os"
    18  	"strconv"
    19  	"sync"
    20  	"time"
    21  
    22  	"github.com/go-openapi/loads"
    23  	"github.com/go-openapi/runtime/middleware"
    24  	"github.com/go-openapi/swag"
    25  	"github.com/sirupsen/logrus"
    26  	"github.com/spf13/pflag"
    27  	"golang.org/x/net/netutil"
    28  
    29  	"github.com/cilium/cilium/api/v1/operator/server/restapi"
    30  	"github.com/cilium/cilium/api/v1/operator/server/restapi/cluster"
    31  	"github.com/cilium/cilium/api/v1/operator/server/restapi/metrics"
    32  	"github.com/cilium/cilium/api/v1/operator/server/restapi/operator"
    33  	"github.com/cilium/hive/cell"
    34  
    35  	"github.com/cilium/cilium/pkg/api"
    36  	"github.com/cilium/cilium/pkg/hive"
    37  )
    38  
    39  // Cell implements the cilium operator REST API server when provided
    40  // the required request handlers.
    41  var Cell = cell.Module(
    42  	"cilium-operator-server",
    43  	"cilium operator server",
    44  
    45  	cell.Provide(newForCell),
    46  	APICell,
    47  )
    48  
    49  // APICell provides the restapi.CiliumOperatorAPI type, populated
    50  // with the request handlers. This cell is an alternative to 'Cell' when only
    51  // the API type is required and not the full server implementation.
    52  var APICell = cell.Provide(newAPI)
    53  
    54  type apiParams struct {
    55  	cell.In
    56  
    57  	Spec *Spec
    58  
    59  	Middleware middleware.Builder `name:"cilium-operator-middleware" optional:"true"`
    60  
    61  	ClusterGetClusterHandler  cluster.GetClusterHandler
    62  	OperatorGetHealthzHandler operator.GetHealthzHandler
    63  	MetricsGetMetricsHandler  metrics.GetMetricsHandler
    64  }
    65  
    66  func newAPI(p apiParams) *restapi.CiliumOperatorAPI {
    67  	api := restapi.NewCiliumOperatorAPI(p.Spec.Document)
    68  
    69  	// Construct the API from the provided handlers
    70  
    71  	api.ClusterGetClusterHandler = p.ClusterGetClusterHandler
    72  	api.OperatorGetHealthzHandler = p.OperatorGetHealthzHandler
    73  	api.MetricsGetMetricsHandler = p.MetricsGetMetricsHandler
    74  
    75  	// Inject custom middleware if provided by Hive
    76  	if p.Middleware != nil {
    77  		api.Middleware = func(builder middleware.Builder) http.Handler {
    78  			return p.Middleware(api.Context().APIHandler(builder))
    79  		}
    80  	}
    81  
    82  	return api
    83  }
    84  
    85  type serverParams struct {
    86  	cell.In
    87  
    88  	Lifecycle  cell.Lifecycle
    89  	Shutdowner hive.Shutdowner
    90  	Logger     logrus.FieldLogger
    91  	Spec       *Spec
    92  	API        *restapi.CiliumOperatorAPI
    93  }
    94  
    95  func newForCell(p serverParams) (*Server, error) {
    96  	s := NewServer(p.API)
    97  	s.shutdowner = p.Shutdowner
    98  	s.logger = p.Logger
    99  	p.Lifecycle.Append(s)
   100  	return s, nil
   101  }
   102  
   103  const (
   104  	schemeHTTP  = "http"
   105  	schemeHTTPS = "https"
   106  	schemeUnix  = "unix"
   107  )
   108  
   109  var defaultSchemes []string
   110  
   111  func init() {
   112  	defaultSchemes = []string{
   113  		schemeHTTP,
   114  		schemeUnix,
   115  	}
   116  }
   117  
   118  var (
   119  	enabledListeners []string
   120  	gracefulTimeout  time.Duration
   121  	maxHeaderSize    int
   122  
   123  	socketPath string
   124  
   125  	host         string
   126  	port         int
   127  	listenLimit  int
   128  	keepAlive    time.Duration
   129  	readTimeout  time.Duration
   130  	writeTimeout time.Duration
   131  
   132  	tlsHost           string
   133  	tlsPort           int
   134  	tlsListenLimit    int
   135  	tlsKeepAlive      time.Duration
   136  	tlsReadTimeout    time.Duration
   137  	tlsWriteTimeout   time.Duration
   138  	tlsCertificate    string
   139  	tlsCertificateKey string
   140  	tlsCACertificate  string
   141  )
   142  
   143  type ServerConfig struct {
   144  	EnableCiliumOperatorServerAccess []string
   145  }
   146  
   147  var (
   148  	defaultServerConfig = ServerConfig{
   149  		EnableCiliumOperatorServerAccess: []string{"*"},
   150  	}
   151  	AdminEnableFlag = "enable-cilium-operator-server-access"
   152  )
   153  
   154  func (cfg ServerConfig) Flags(flags *pflag.FlagSet) {
   155  	flags.StringSlice(AdminEnableFlag, cfg.EnableCiliumOperatorServerAccess,
   156  		"List of cilium operator APIs which are administratively enabled. Supports '*'.")
   157  }
   158  
   159  var SpecCell = cell.Module(
   160  	"cilium-operator-spec",
   161  	"cilium operator Specification",
   162  
   163  	cell.Config(defaultServerConfig),
   164  	cell.Provide(newSpec),
   165  )
   166  
   167  type Spec struct {
   168  	*loads.Document
   169  
   170  	// DeniedAPIs is a set of APIs that are administratively disabled.
   171  	DeniedAPIs api.PathSet
   172  }
   173  
   174  func newSpec(cfg ServerConfig) (*Spec, error) {
   175  	swaggerSpec, err := loads.Analyzed(SwaggerJSON, "")
   176  	if err != nil {
   177  		return nil, fmt.Errorf("failed to load swagger spec: %w", err)
   178  	}
   179  
   180  	deniedAPIs, err := api.AllowedFlagsToDeniedPaths(swaggerSpec, cfg.EnableCiliumOperatorServerAccess)
   181  	if err != nil {
   182  		return nil, fmt.Errorf("failed to parse %q flag: %w",
   183  			AdminEnableFlag, err)
   184  	}
   185  
   186  	return &Spec{
   187  		Document:   swaggerSpec,
   188  		DeniedAPIs: deniedAPIs,
   189  	}, nil
   190  }
   191  
   192  // NewServer creates a new api cilium operator server but does not configure it
   193  func NewServer(api *restapi.CiliumOperatorAPI) *Server {
   194  	s := new(Server)
   195  	s.api = api
   196  	return s
   197  }
   198  
   199  // ConfigureAPI configures the API and handlers.
   200  func (s *Server) ConfigureAPI() {
   201  	if s.api != nil {
   202  		s.handler = configureAPI(s.api)
   203  	}
   204  }
   205  
   206  // ConfigureFlags configures the additional flags defined by the handlers. Needs to be called before the parser.Parse
   207  func (s *Server) ConfigureFlags() {
   208  	if s.api != nil {
   209  		configureFlags(s.api)
   210  	}
   211  }
   212  
   213  // Server for the cilium operator API
   214  type Server struct {
   215  	EnabledListeners []string
   216  	CleanupTimeout   time.Duration
   217  	GracefulTimeout  time.Duration
   218  	MaxHeaderSize    int
   219  
   220  	SocketPath    string
   221  	domainSocketL *net.UnixListener
   222  
   223  	Host         string
   224  	Port         int
   225  	ListenLimit  int
   226  	KeepAlive    time.Duration
   227  	ReadTimeout  time.Duration
   228  	WriteTimeout time.Duration
   229  	httpServerL  net.Listener
   230  
   231  	TLSHost           string
   232  	TLSPort           int
   233  	TLSCertificate    string
   234  	TLSCertificateKey string
   235  	TLSCACertificate  string
   236  	TLSListenLimit    int
   237  	TLSKeepAlive      time.Duration
   238  	TLSReadTimeout    time.Duration
   239  	TLSWriteTimeout   time.Duration
   240  	httpsServerL      net.Listener
   241  
   242  	api          *restapi.CiliumOperatorAPI
   243  	handler      http.Handler
   244  	hasListeners bool
   245  	servers      []*http.Server
   246  
   247  	wg         sync.WaitGroup
   248  	shutdowner hive.Shutdowner
   249  	logger     logrus.FieldLogger
   250  }
   251  
   252  // Logf logs message either via defined user logger or via system one if no user logger is defined.
   253  func (s *Server) Logf(f string, args ...interface{}) {
   254  	if s.logger != nil {
   255  		s.logger.Infof(f, args...)
   256  	} else if s.api != nil && s.api.Logger != nil {
   257  		s.api.Logger(f, args...)
   258  	} else {
   259  		log.Printf(f, args...)
   260  	}
   261  }
   262  
   263  // Fatalf logs message either via defined user logger or via system one if no user logger is defined.
   264  // Exits with non-zero status after printing
   265  func (s *Server) Fatalf(f string, args ...interface{}) {
   266  	if s.shutdowner != nil {
   267  		s.shutdowner.Shutdown(hive.ShutdownWithError(fmt.Errorf(f, args...)))
   268  	} else if s.api != nil && s.api.Logger != nil {
   269  		s.api.Logger(f, args...)
   270  		os.Exit(1)
   271  	} else {
   272  		log.Fatalf(f, args...)
   273  	}
   274  }
   275  
   276  // SetAPI configures the server with the specified API. Needs to be called before Serve
   277  func (s *Server) SetAPI(api *restapi.CiliumOperatorAPI) {
   278  	if api == nil {
   279  		s.api = nil
   280  		s.handler = nil
   281  		return
   282  	}
   283  
   284  	s.api = api
   285  	s.handler = configureAPI(api)
   286  }
   287  
   288  // GetAPI returns the configured API. Modifications on the API must be performed
   289  // before server is started.
   290  func (s *Server) GetAPI() *restapi.CiliumOperatorAPI {
   291  	return s.api
   292  }
   293  
   294  func (s *Server) hasScheme(scheme string) bool {
   295  	schemes := s.EnabledListeners
   296  	if len(schemes) == 0 {
   297  		schemes = defaultSchemes
   298  	}
   299  
   300  	for _, v := range schemes {
   301  		if v == scheme {
   302  			return true
   303  		}
   304  	}
   305  	return false
   306  }
   307  
   308  func (s *Server) Serve() error {
   309  	// TODO remove when this is not needed for compatibility anymore
   310  	if err := s.Start(context.TODO()); err != nil {
   311  		return err
   312  	}
   313  	s.wg.Wait()
   314  	return nil
   315  }
   316  
   317  // Start the server
   318  func (s *Server) Start(cell.HookContext) (err error) {
   319  	if !s.hasListeners {
   320  		if err = s.Listen(); err != nil {
   321  			return err
   322  		}
   323  	}
   324  
   325  	if len(s.servers) != 0 {
   326  		return errors.New("already started")
   327  	}
   328  
   329  	// set default handler, if none is set
   330  	if s.handler == nil {
   331  		if s.api == nil {
   332  			return errors.New("can't create the default handler, as no api is set")
   333  		}
   334  
   335  		s.ConfigureAPI()
   336  		s.SetHandler(s.api.Serve(nil))
   337  	}
   338  
   339  	if s.hasScheme(schemeUnix) {
   340  		domainSocket := new(http.Server)
   341  		domainSocket.MaxHeaderBytes = s.MaxHeaderSize
   342  		domainSocket.Handler = s.handler
   343  		if int64(s.CleanupTimeout) > 0 {
   344  			domainSocket.IdleTimeout = s.CleanupTimeout
   345  		}
   346  
   347  		configureServer(domainSocket, "unix", s.SocketPath)
   348  
   349  		if os.Getuid() == 0 {
   350  			err := api.SetDefaultPermissions(s.SocketPath)
   351  			if err != nil {
   352  				return err
   353  			}
   354  		}
   355  		s.servers = append(s.servers, domainSocket)
   356  		s.wg.Add(1)
   357  		s.Logf("Serving cilium operator at unix://%s", s.SocketPath)
   358  		go func(l net.Listener) {
   359  			defer s.wg.Done()
   360  			if err := domainSocket.Serve(l); err != nil && err != http.ErrServerClosed {
   361  				s.Fatalf("%v", err)
   362  			}
   363  			s.Logf("Stopped serving cilium operator at unix://%s", s.SocketPath)
   364  		}(s.domainSocketL)
   365  	}
   366  
   367  	if s.hasScheme(schemeHTTP) {
   368  		httpServer := new(http.Server)
   369  		httpServer.MaxHeaderBytes = s.MaxHeaderSize
   370  		httpServer.ReadTimeout = s.ReadTimeout
   371  		httpServer.WriteTimeout = s.WriteTimeout
   372  		httpServer.SetKeepAlivesEnabled(int64(s.KeepAlive) > 0)
   373  		if s.ListenLimit > 0 {
   374  			s.httpServerL = netutil.LimitListener(s.httpServerL, s.ListenLimit)
   375  		}
   376  
   377  		if int64(s.CleanupTimeout) > 0 {
   378  			httpServer.IdleTimeout = s.CleanupTimeout
   379  		}
   380  
   381  		httpServer.Handler = s.handler
   382  
   383  		configureServer(httpServer, "http", s.httpServerL.Addr().String())
   384  
   385  		s.servers = append(s.servers, httpServer)
   386  		s.wg.Add(1)
   387  		s.Logf("Serving cilium operator at http://%s", s.httpServerL.Addr())
   388  		go func(l net.Listener) {
   389  			defer s.wg.Done()
   390  			if err := httpServer.Serve(l); err != nil && err != http.ErrServerClosed {
   391  				s.Fatalf("%v", err)
   392  			}
   393  			s.Logf("Stopped serving cilium operator at http://%s", l.Addr())
   394  		}(s.httpServerL)
   395  	}
   396  
   397  	if s.hasScheme(schemeHTTPS) {
   398  		httpsServer := new(http.Server)
   399  		httpsServer.MaxHeaderBytes = s.MaxHeaderSize
   400  		httpsServer.ReadTimeout = s.TLSReadTimeout
   401  		httpsServer.WriteTimeout = s.TLSWriteTimeout
   402  		httpsServer.SetKeepAlivesEnabled(int64(s.TLSKeepAlive) > 0)
   403  		if s.TLSListenLimit > 0 {
   404  			s.httpsServerL = netutil.LimitListener(s.httpsServerL, s.TLSListenLimit)
   405  		}
   406  		if int64(s.CleanupTimeout) > 0 {
   407  			httpsServer.IdleTimeout = s.CleanupTimeout
   408  		}
   409  		httpsServer.Handler = s.handler
   410  
   411  		// Inspired by https://blog.bracebin.com/achieving-perfect-ssl-labs-score-with-go
   412  		httpsServer.TLSConfig = &tls.Config{
   413  			// Causes servers to use Go's default ciphersuite preferences,
   414  			// which are tuned to avoid attacks. Does nothing on clients.
   415  			PreferServerCipherSuites: true,
   416  			// Only use curves which have assembly implementations
   417  			// https://github.com/golang/go/tree/master/src/crypto/elliptic
   418  			CurvePreferences: []tls.CurveID{tls.CurveP256},
   419  			// Use modern tls mode https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
   420  			NextProtos: []string{"h2", "http/1.1"},
   421  			// https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Only_Support_Strong_Protocols
   422  			MinVersion: tls.VersionTLS12,
   423  			// These ciphersuites support Forward Secrecy: https://en.wikipedia.org/wiki/Forward_secrecy
   424  			CipherSuites: []uint16{
   425  				tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
   426  				tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
   427  				tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
   428  				tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
   429  				tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
   430  				tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
   431  			},
   432  		}
   433  
   434  		// build standard config from server options
   435  		if s.TLSCertificate != "" && s.TLSCertificateKey != "" {
   436  			httpsServer.TLSConfig.Certificates = make([]tls.Certificate, 1)
   437  			httpsServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(s.TLSCertificate, s.TLSCertificateKey)
   438  			if err != nil {
   439  				return err
   440  			}
   441  		}
   442  
   443  		if s.TLSCACertificate != "" {
   444  			// include specified CA certificate
   445  			caCert, caCertErr := os.ReadFile(s.TLSCACertificate)
   446  			if caCertErr != nil {
   447  				return caCertErr
   448  			}
   449  			caCertPool := x509.NewCertPool()
   450  			ok := caCertPool.AppendCertsFromPEM(caCert)
   451  			if !ok {
   452  				return fmt.Errorf("cannot parse CA certificate")
   453  			}
   454  			httpsServer.TLSConfig.ClientCAs = caCertPool
   455  			httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
   456  		}
   457  
   458  		// call custom TLS configurator
   459  		configureTLS(httpsServer.TLSConfig)
   460  
   461  		if len(httpsServer.TLSConfig.Certificates) == 0 && httpsServer.TLSConfig.GetCertificate == nil {
   462  			// after standard and custom config are passed, this ends up with no certificate
   463  			if s.TLSCertificate == "" {
   464  				if s.TLSCertificateKey == "" {
   465  					s.Fatalf("the required flags `--tls-certificate` and `--tls-key` were not specified")
   466  				}
   467  				s.Fatalf("the required flag `--tls-certificate` was not specified")
   468  			}
   469  			if s.TLSCertificateKey == "" {
   470  				s.Fatalf("the required flag `--tls-key` was not specified")
   471  			}
   472  			// this happens with a wrong custom TLS configurator
   473  			s.Fatalf("no certificate was configured for TLS")
   474  		}
   475  
   476  		// must have at least one certificate or panics
   477  		httpsServer.TLSConfig.BuildNameToCertificate()
   478  
   479  		configureServer(httpsServer, "https", s.httpsServerL.Addr().String())
   480  
   481  		s.servers = append(s.servers, httpsServer)
   482  		s.wg.Add(1)
   483  		s.Logf("Serving cilium operator at https://%s", s.httpsServerL.Addr())
   484  		go func(l net.Listener) {
   485  			defer s.wg.Done()
   486  			if err := httpsServer.Serve(l); err != nil && err != http.ErrServerClosed {
   487  				s.Fatalf("%v", err)
   488  			}
   489  			s.Logf("Stopped serving cilium operator at https://%s", l.Addr())
   490  		}(tls.NewListener(s.httpsServerL, httpsServer.TLSConfig))
   491  	}
   492  
   493  	return nil
   494  }
   495  
   496  // Listen creates the listeners for the server
   497  func (s *Server) Listen() error {
   498  	if s.hasListeners { // already done this
   499  		return nil
   500  	}
   501  
   502  	if s.hasScheme(schemeHTTPS) {
   503  		// Use http host if https host wasn't defined
   504  		if s.TLSHost == "" {
   505  			s.TLSHost = s.Host
   506  		}
   507  		// Use http listen limit if https listen limit wasn't defined
   508  		if s.TLSListenLimit == 0 {
   509  			s.TLSListenLimit = s.ListenLimit
   510  		}
   511  		// Use http tcp keep alive if https tcp keep alive wasn't defined
   512  		if int64(s.TLSKeepAlive) == 0 {
   513  			s.TLSKeepAlive = s.KeepAlive
   514  		}
   515  		// Use http read timeout if https read timeout wasn't defined
   516  		if int64(s.TLSReadTimeout) == 0 {
   517  			s.TLSReadTimeout = s.ReadTimeout
   518  		}
   519  		// Use http write timeout if https write timeout wasn't defined
   520  		if int64(s.TLSWriteTimeout) == 0 {
   521  			s.TLSWriteTimeout = s.WriteTimeout
   522  		}
   523  	}
   524  
   525  	if s.hasScheme(schemeUnix) {
   526  		addr, err := net.ResolveUnixAddr("unix", s.SocketPath)
   527  		if err != nil {
   528  			return err
   529  		}
   530  		domSockListener, err := net.ListenUnix("unix", addr)
   531  		if err != nil {
   532  			return err
   533  		}
   534  		s.domainSocketL = domSockListener
   535  	}
   536  
   537  	if s.hasScheme(schemeHTTP) {
   538  		listener, err := net.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port)))
   539  		if err != nil {
   540  			return err
   541  		}
   542  
   543  		h, p, err := swag.SplitHostPort(listener.Addr().String())
   544  		if err != nil {
   545  			return err
   546  		}
   547  		s.Host = h
   548  		s.Port = p
   549  		s.httpServerL = listener
   550  	}
   551  
   552  	if s.hasScheme(schemeHTTPS) {
   553  		tlsListener, err := net.Listen("tcp", net.JoinHostPort(s.TLSHost, strconv.Itoa(s.TLSPort)))
   554  		if err != nil {
   555  			return err
   556  		}
   557  
   558  		sh, sp, err := swag.SplitHostPort(tlsListener.Addr().String())
   559  		if err != nil {
   560  			return err
   561  		}
   562  		s.TLSHost = sh
   563  		s.TLSPort = sp
   564  		s.httpsServerL = tlsListener
   565  	}
   566  
   567  	s.hasListeners = true
   568  	return nil
   569  }
   570  
   571  // Shutdown server and clean up resources
   572  func (s *Server) Shutdown() error {
   573  	ctx, cancel := context.WithTimeout(context.TODO(), s.GracefulTimeout)
   574  	defer cancel()
   575  	return s.Stop(ctx)
   576  }
   577  
   578  func (s *Server) Stop(ctx cell.HookContext) error {
   579  	// first execute the pre-shutdown hook
   580  	s.api.PreServerShutdown()
   581  
   582  	shutdownChan := make(chan bool)
   583  	for i := range s.servers {
   584  		server := s.servers[i]
   585  		go func() {
   586  			var success bool
   587  			defer func() {
   588  				shutdownChan <- success
   589  			}()
   590  			if err := server.Shutdown(ctx); err != nil {
   591  				s.Logf("HTTP server Shutdown: %v", err)
   592  
   593  				// Forcefully close open connections.
   594  				server.Close()
   595  			} else {
   596  				success = true
   597  			}
   598  		}()
   599  	}
   600  
   601  	// Wait until all listeners have successfully shut down before calling ServerShutdown
   602  	success := true
   603  	for range s.servers {
   604  		success = success && <-shutdownChan
   605  	}
   606  	if success {
   607  		s.api.ServerShutdown()
   608  	}
   609  
   610  	s.wg.Wait()
   611  	s.servers = nil
   612  
   613  	return nil
   614  }
   615  
   616  // GetHandler returns a handler useful for testing
   617  func (s *Server) GetHandler() http.Handler {
   618  	return s.handler
   619  }
   620  
   621  // SetHandler allows for setting a http handler on this server
   622  func (s *Server) SetHandler(handler http.Handler) {
   623  	s.handler = handler
   624  }
   625  
   626  // UnixListener returns the domain socket listener
   627  func (s *Server) UnixListener() (*net.UnixListener, error) {
   628  	if !s.hasListeners {
   629  		if err := s.Listen(); err != nil {
   630  			return nil, err
   631  		}
   632  	}
   633  	return s.domainSocketL, nil
   634  }
   635  
   636  // HTTPListener returns the http listener
   637  func (s *Server) HTTPListener() (net.Listener, error) {
   638  	if !s.hasListeners {
   639  		if err := s.Listen(); err != nil {
   640  			return nil, err
   641  		}
   642  	}
   643  	return s.httpServerL, nil
   644  }
   645  
   646  // TLSListener returns the https listener
   647  func (s *Server) TLSListener() (net.Listener, error) {
   648  	if !s.hasListeners {
   649  		if err := s.Listen(); err != nil {
   650  			return nil, err
   651  		}
   652  	}
   653  	return s.httpsServerL, nil
   654  }