github.com/Equinix-Metal/virtlet@v1.5.2-0.20210807010419-342346535dc5/cmd/virtlet/virtlet.go (about)

     1  /*
     2  Copyright 2017 Mirantis
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	goflag "flag"
    21  	"fmt"
    22  	"math/rand"
    23  	"os"
    24  	"time"
    25  
    26  	"github.com/golang/glog"
    27  	flag "github.com/spf13/pflag"
    28  	"k8s.io/client-go/tools/clientcmd"
    29  
    30  	"github.com/Equinix-Metal/virtlet/pkg/api/virtlet.k8s/v1"
    31  	"github.com/Equinix-Metal/virtlet/pkg/cni"
    32  	"github.com/Equinix-Metal/virtlet/pkg/config"
    33  	"github.com/Equinix-Metal/virtlet/pkg/diag"
    34  	"github.com/Equinix-Metal/virtlet/pkg/fs"
    35  	"github.com/Equinix-Metal/virtlet/pkg/manager"
    36  	"github.com/Equinix-Metal/virtlet/pkg/nsfix"
    37  	"github.com/Equinix-Metal/virtlet/pkg/tapmanager"
    38  	"github.com/Equinix-Metal/virtlet/pkg/utils"
    39  	"github.com/Equinix-Metal/virtlet/pkg/version"
    40  )
    41  
    42  const (
    43  	nodeNameEnv        = "KUBE_NODE_NAME"
    44  	diagSocket         = "/run/virtlet-diag.sock"
    45  	netnsDiagCommand   = `if [ -d /var/run/netns ]; then cd /var/run/netns; for ns in *; do echo "*** ${ns} ***"; ip netns exec "${ns}" ip a; ip netns exec "${ns}" ip r; echo; done; fi`
    46  	criproxyLogCommand = `nsenter -t 1 -m -u -i journalctl -xe -u criproxy -n 20000 --no-pager || true`
    47  	qemuLogDir         = "/var/log/libvirt/qemu"
    48  )
    49  
    50  var (
    51  	dumpConfig     = flag.Bool("dump-config", false, "Dump node-specific Virtlet config as a shell script and exit")
    52  	dumpDiag       = flag.Bool("diag", false, "Dump diagnostics as JSON and exit")
    53  	displayVersion = flag.Bool("version", false, "Display version and exit")
    54  	versionFormat  = flag.String("version-format", "text", "Version format to use (text, short, json, yaml)")
    55  )
    56  
    57  func configWithDefaults(cfg *v1.VirtletConfig) *v1.VirtletConfig {
    58  	r := config.GetDefaultConfig()
    59  	config.Override(r, cfg)
    60  	return r
    61  }
    62  
    63  func runVirtlet(config *v1.VirtletConfig, clientCfg clientcmd.ClientConfig, diagSet *diag.Set) {
    64  	manager := manager.NewVirtletManager(config, nil, clientCfg, diagSet)
    65  	if err := manager.Run(); err != nil {
    66  		glog.Errorf("Error: %v", err)
    67  		os.Exit(1)
    68  	}
    69  }
    70  
    71  func runTapManager(config *v1.VirtletConfig) {
    72  	cniClient, err := cni.NewClient(*config.CNIPluginDir, *config.CNIConfigDir)
    73  	if err != nil {
    74  		glog.Errorf("Error initializing CNI client: %v", err)
    75  		os.Exit(1)
    76  	}
    77  	src, err := tapmanager.NewTapFDSource(cniClient, *config.EnableSriov, *config.CalicoSubnetSize)
    78  	if err != nil {
    79  		glog.Errorf("Error creating tap fd source: %v", err)
    80  		os.Exit(1)
    81  	}
    82  	os.Remove(*config.FDServerSocketPath) // FIXME
    83  	s := tapmanager.NewFDServer(*config.FDServerSocketPath, src)
    84  	if err = s.Serve(); err != nil {
    85  		glog.Errorf("FD server returned error: %v", err)
    86  		os.Exit(1)
    87  	}
    88  	if err := fs.RealFileSystem.ChownForEmulator(*config.FDServerSocketPath, false); err != nil {
    89  		glog.Warningf("Couldn't set tapmanager socket permissions: %v", err)
    90  	}
    91  	for {
    92  		time.Sleep(1000 * time.Hour)
    93  	}
    94  }
    95  
    96  func printVersion() {
    97  	out, err := version.Get().ToBytes(*versionFormat)
    98  	if err == nil {
    99  		_, err = os.Stdout.Write(out)
   100  	}
   101  	if err != nil {
   102  		glog.Errorf("Error printing version info: %v", err)
   103  		os.Exit(1)
   104  	}
   105  }
   106  
   107  func setLogLevel(config *v1.VirtletConfig) {
   108  	goflag.CommandLine.Parse([]string{
   109  		fmt.Sprintf("-v=%d", *config.LogLevel),
   110  		"-logtostderr=true",
   111  	})
   112  }
   113  
   114  func runDiagServer() *diag.Set {
   115  	diagSet := diag.NewDiagSet()
   116  	diagSet.RegisterDiagSource("ip-a", diag.NewCommandSource("txt", []string{"ip", "a"}))
   117  	diagSet.RegisterDiagSource("ip-r", diag.NewCommandSource("txt", []string{"ip", "r"}))
   118  	diagSet.RegisterDiagSource("psaux", diag.NewCommandSource("txt", []string{"ps", "aux"}))
   119  	diagSet.RegisterDiagSource("netns", diag.NewCommandSource("txt", []string{"/bin/bash", "-c", netnsDiagCommand}))
   120  	diagSet.RegisterDiagSource("criproxy", diag.NewCommandSource("log", []string{"/bin/bash", "-c", criproxyLogCommand}))
   121  	diagSet.RegisterDiagSource("libvirt-logs", diag.NewLogDirSource(qemuLogDir))
   122  	diagSet.RegisterDiagSource("stack", diag.StackDumpSource)
   123  	server := diag.NewServer(diagSet)
   124  	go func() {
   125  		err := server.Serve(diagSocket, nil)
   126  		glog.V(1).Infof("Diag server returned: %v", err)
   127  	}()
   128  	return diagSet
   129  }
   130  
   131  func doDiag() {
   132  	dr, err := diag.RetrieveDiagnostics(diagSocket)
   133  	if err != nil {
   134  		glog.Errorf("Failed to retrieve diagnostics: %v", err)
   135  		os.Exit(1)
   136  	}
   137  	os.Stdout.Write(dr.ToJSON())
   138  }
   139  
   140  func main() {
   141  	nsfix.HandleReexec()
   142  	clientCfg := utils.BindFlags(flag.CommandLine)
   143  	var cb *config.Binder
   144  	cb = config.NewBinder(flag.CommandLine)
   145  	flag.Parse()
   146  	localConfig := cb.GetConfig()
   147  
   148  	rand.Seed(time.Now().UnixNano())
   149  	setLogLevel(configWithDefaults(localConfig))
   150  	switch {
   151  	case *displayVersion:
   152  		printVersion()
   153  	case *dumpConfig:
   154  		nodeConfig := config.NewNodeConfig(clientCfg)
   155  		nodeName := os.Getenv(nodeNameEnv)
   156  		cfg, err := nodeConfig.LoadConfig(localConfig, nodeName)
   157  		if err != nil {
   158  			glog.Warningf("Failed to load per-node configs, using local config only: %v", err)
   159  			cfg = localConfig
   160  		}
   161  		if _, err := os.Stdout.Write([]byte(config.DumpEnv(cfg))); err != nil {
   162  			glog.Errorf("Error writing config: %v", err)
   163  			os.Exit(1)
   164  		}
   165  	case *dumpDiag:
   166  		doDiag()
   167  	default:
   168  		localConfig = configWithDefaults(localConfig)
   169  		go runTapManager(localConfig)
   170  		diagSet := runDiagServer()
   171  		runVirtlet(localConfig, clientCfg, diagSet)
   172  	}
   173  }