github.com/mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/tools/virsh.go (about) 1 /* 2 Copyright 2018 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 tools 18 19 import ( 20 "errors" 21 "fmt" 22 "io" 23 "os" 24 25 "github.com/renstrom/dedent" 26 "github.com/spf13/cobra" 27 ) 28 29 // virshCommand contains the data needed by the virsh subcommand 30 // which allows one to execute virsh commands for a VM pod. 31 type virshCommand struct { 32 client KubeClient 33 nodeName string 34 args []string 35 out io.Writer 36 realArgs []string 37 domainNodeName string 38 virtletPodName string 39 } 40 41 // NewVirshCmd returns a cobra.Command that executes virsh for a VM pod. 42 func NewVirshCmd(client KubeClient, out io.Writer) *cobra.Command { 43 virsh := &virshCommand{client: client, out: out} 44 cmd := &cobra.Command{ 45 Use: "virsh [flags] virsh_command -- [virsh_command_args...]", 46 Short: "Execute a virsh command", 47 Long: dedent.Dedent(` 48 This command executes libvirt virsh command. 49 50 A VM pod name in the form @podname is translated to the 51 corresponding libvirt domain name. If @podname is specified, 52 the target k8s node name is inferred automatically based 53 on the information of the VM pod. In case if no @podname 54 is specified, the command is executed on every node 55 and the output for every node is prepended with a line 56 with the node name and corresponding Virtlet pod name.`), 57 RunE: func(cmd *cobra.Command, args []string) error { 58 virsh.args = args 59 return virsh.Run() 60 }, 61 } 62 cmd.Flags().StringVar(&virsh.nodeName, "node", "", "the name of the target node") 63 return cmd 64 } 65 66 func (v *virshCommand) processArgs() error { 67 if len(v.args) == 0 { 68 return errors.New("missing virsh argument(s)") 69 } 70 71 v.realArgs = nil 72 for _, arg := range v.args { 73 if len(arg) < 2 || arg[0] != '@' { 74 v.realArgs = append(v.realArgs, arg) 75 continue 76 } 77 podName := arg[1:] 78 vmPodInfo, err := v.client.GetVMPodInfo(podName) 79 switch { 80 case err != nil: 81 return fmt.Errorf("can't get VM pod info for %q: %v", podName, err) 82 case v.domainNodeName == "": 83 v.domainNodeName = vmPodInfo.NodeName 84 v.virtletPodName = vmPodInfo.VirtletPodName 85 case v.domainNodeName != vmPodInfo.NodeName: 86 return errors.New("can't reference VM pods that run on different nodes") 87 } 88 v.realArgs = append(v.realArgs, vmPodInfo.LibvirtDomainName()) 89 } 90 91 return nil 92 } 93 94 func (v *virshCommand) runInVirtletPod(virtletPodName string) error { 95 exitCode, err := v.client.ExecInContainer(virtletPodName, "libvirt", "kube-system", nil, v.out, os.Stderr, append([]string{"virsh"}, v.realArgs...)) 96 if err != nil { 97 return fmt.Errorf("error executing virsh in Virtlet pod %q: %v", virtletPodName, err) 98 } 99 if exitCode != 0 { 100 return fmt.Errorf("virsh returned non-zero exit code %d", exitCode) 101 } 102 return nil 103 } 104 105 func (v *virshCommand) runOnAllNodes() error { 106 podNames, nodeNames, err := v.client.GetVirtletPodAndNodeNames() 107 if err != nil { 108 return err 109 } 110 gotErrors := false 111 for n, nodeName := range nodeNames { 112 fmt.Fprintf(v.out, "*** node: %s pod: %s ***\n", nodeName, podNames[n]) 113 if err := v.runInVirtletPod(podNames[n]); err != nil { 114 fmt.Fprintf(v.out, "ERROR: %v\n", err) 115 gotErrors = true 116 } 117 fmt.Fprint(v.out, "\n") 118 } 119 if gotErrors { 120 return errors.New("some of the nodes returned errors") 121 } 122 return nil 123 } 124 125 // Run executes the command. 126 func (v *virshCommand) Run() error { 127 if err := v.processArgs(); err != nil { 128 return err 129 } 130 switch { 131 case v.nodeName == "" && v.domainNodeName == "": 132 return v.runOnAllNodes() 133 case v.domainNodeName == "": 134 var err error 135 v.virtletPodName, err = v.client.GetVirtletPodNameForNode(v.nodeName) 136 if err != nil { 137 return fmt.Errorf("couldn't get Virtlet pod name for node %q: %v", v.nodeName, err) 138 } 139 case v.nodeName != "" && v.domainNodeName != v.nodeName: 140 return errors.New("--node specifies a node other than one that runs the VM pod") 141 } 142 143 return v.runInVirtletPod(v.virtletPodName) 144 }