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 }