func cliVmConfig(c *minicli.Command, resp *minicli.Response) error { if c.BoolArgs["save"] { // Save the current config savedInfo[c.StringArgs["name"]] = vmConfig.Copy() return nil } else if c.BoolArgs["restore"] { if name, ok := c.StringArgs["name"]; ok { // Try to restore an existing config if _, ok := savedInfo[name]; !ok { return fmt.Errorf("config %v does not exist", name) } vmConfig = savedInfo[name].Copy() return nil } else if len(savedInfo) == 0 { return errors.New("no vm configs saved") } // List the save configs for k := range savedInfo { resp.Response += fmt.Sprintln(k) } return nil } else if c.BoolArgs["clone"] { // Clone the config of an existing vm vm := vms.FindVM(c.StringArgs["vm"]) if vm == nil { return vmNotFound(c.StringArgs["vm"]) } switch vm := vm.(type) { case *KvmVM: vmConfig.BaseConfig = vm.BaseConfig.Copy() vmConfig.KVMConfig = vm.KVMConfig.Copy() case *ContainerVM: vmConfig.BaseConfig = vm.BaseConfig.Copy() vmConfig.ContainerConfig = vm.ContainerConfig.Copy() } return nil } // Print the config resp.Response = vmConfig.String() return nil }
func cliVmConfigTag(c *minicli.Command, resp *minicli.Response) error { k := c.StringArgs["key"] if v, ok := c.StringArgs["value"]; ok { // Setting a new value vmConfig.Tags[k] = v } else if k != "" { // Printing a single tag resp.Response = vmConfig.Tags[k] } else { // Printing all configured tags resp.Response = vmConfig.TagsString() } return nil }
func cliHost(c *minicli.Command, resp *minicli.Response) error { // If they selected one of the fields to display for k := range c.BoolArgs { val, err := hostInfoFns[k]() if err != nil { return err } resp.Response = val return nil } // Must want all fields resp.Header = hostInfoKeys row := []string{} for _, k := range resp.Header { val, err := hostInfoFns[k]() if err != nil { return err } row = append(row, val) } resp.Tabular = [][]string{row} return nil }
func cliVmConfigField(c *minicli.Command, resp *minicli.Response, field string) error { // If there are no args it means that we want to display the current value nArgs := len(c.StringArgs) + len(c.ListArgs) + len(c.BoolArgs) var ok bool var fns VMConfigFns var config interface{} // Find the right config functions, baseConfigFns has highest priority if fns, ok = baseConfigFns[field]; ok { config = &vmConfig.BaseConfig } else if fns, ok = kvmConfigFns[field]; ok { config = &vmConfig.KVMConfig } else if fns, ok = containerConfigFns[field]; ok { config = &vmConfig.ContainerConfig } else { return fmt.Errorf("unknown config field: `%s`", field) } if nArgs == 0 { resp.Response = fns.Print(config) return nil } return fns.Update(config, c) }
func cliLogFile(c *minicli.Command, resp *minicli.Response) error { if len(c.StringArgs) == 0 { // Print true or false depending on whether file is enabled if logFile != nil { resp.Response = logFile.Name() } return nil } // Enable logging to file if it's not already enabled level, _ := log.LevelInt(*f_loglevel) if logFile != nil { if err := stopFileLogger(); err != nil { return err } } err := os.MkdirAll(filepath.Dir(c.StringArgs["file"]), 0755) if err != nil { return err } flags := os.O_WRONLY | os.O_APPEND | os.O_CREATE logFile, err = os.OpenFile(c.StringArgs["file"], flags, 0660) if err != nil { return err } log.AddLogger("file", logFile, level, false) return nil }
func cliHelp(c *minicli.Command, resp *minicli.Response) error { input := "" if args, ok := c.ListArgs["command"]; ok { input = strings.Join(args, " ") } resp.Response = minicli.Help(input) return nil }
// prefix func cliCCPrefix(c *minicli.Command, resp *minicli.Response) error { if prefix, ok := c.StringArgs["prefix"]; ok { ccPrefix = prefix return nil } resp.Response = ccPrefix return nil }
// routines for interfacing bridge mechanisms with the cli func cliHostTap(c *minicli.Command, resp *minicli.Response) error { if c.BoolArgs["create"] { b := c.StringArgs["bridge"] tap, err := hostTapCreate(b, c.StringArgs["tap"], c.StringArgs["vlan"]) if err != nil { return err } if c.BoolArgs["dhcp"] { log.Debug("obtaining dhcp on tap %v", tap) var out string out, err = processWrapper("dhcp", tap) if err != nil { err = fmt.Errorf("dhcp error %v: `%v`", err, out) } } else if c.StringArgs["ip"] != "" { ip := c.StringArgs["ip"] log.Debug("setting ip on tap %v: %v", tap, ip) var out string out, err = processWrapper("ip", "addr", "add", "dev", tap, ip) if err != nil { err = fmt.Errorf("ip error %v: `%v`", err, out) } } if err != nil { // One of the above cases failed, try to clean up the tap if err := hostTapDelete(tap); err != nil { // Welp, we're boned log.Error("zombie tap -- %v %v", tap, err) } return err } // Success! if ns := GetNamespace(); ns != nil { // TODO: probably need lock... ns.Taps[tap] = true } resp.Response = tap return nil } else if c.BoolArgs["delete"] { return hostTapDelete(c.StringArgs["id"]) } // Must be the list command hostTapList(resp) return nil }
func cliShell(c *minicli.Command, resp *minicli.Response, background bool) error { var sOut bytes.Buffer var sErr bytes.Buffer p, err := exec.LookPath(c.ListArgs["command"][0]) if err != nil { return err } args := []string{p} if len(c.ListArgs["command"]) > 1 { args = append(args, c.ListArgs["command"][1:]...) } cmd := &exec.Cmd{ Path: p, Args: args, Env: nil, Dir: "", Stdout: &sOut, Stderr: &sErr, } log.Info("starting: %v", args) if err := cmd.Start(); err != nil { return err } if background { go func() { if err := cmd.Wait(); err != nil { log.Error(err.Error()) return } log.Info("command %v exited", args) if out := sOut.String(); out != "" { log.Info(out) } if err := sErr.String(); err != "" { log.Info(err) } }() return nil } if err = cmd.Wait(); err != nil { return err } resp.Response = sOut.String() resp.Error = sErr.String() return nil }
func cliMeshageList(c *minicli.Command, resp *minicli.Response) error { mesh := meshageNode.Mesh() var keys []string for k, _ := range mesh { keys = append(keys, k) } sort.Strings(keys) for _, key := range keys { v := mesh[key] resp.Response += fmt.Sprintf("%s\n", key) sort.Strings(v) for _, x := range v { resp.Response += fmt.Sprintf(" |--%s\n", x) } } return nil }
// cli commands for meshage control func cliMeshageDegree(c *minicli.Command, resp *minicli.Response) error { if c.StringArgs["degree"] != "" { degree, err := strconv.ParseUint(c.StringArgs["degree"], 0, 10) if err != nil { return err } meshageNode.SetDegree(uint(degree)) return nil } resp.Response = fmt.Sprintf("%d", meshageNode.GetDegree()) return nil }
func cliVmQmp(c *minicli.Command, resp *minicli.Response) error { vm, err := vms.FindKvmVM(c.StringArgs["vm"]) if err != nil { return err } out, err := vm.QMPRaw(c.StringArgs["qmp"]) if err != nil { return err } resp.Response = out return nil }
func cliQuit(c *minicli.Command, resp *minicli.Response) error { if v, ok := c.StringArgs["delay"]; ok { delay, err := strconv.Atoi(v) if err != nil { return err } go func() { time.Sleep(time.Duration(delay) * time.Second) teardown() }() resp.Response = fmt.Sprintf("quitting after %v seconds", delay) return nil } teardown() return errors.New("unreachable") }
func cliMeshageTimeout(c *minicli.Command, resp *minicli.Response) error { if c.StringArgs["timeout"] != "" { timeout, err := strconv.Atoi(c.StringArgs["timeout"]) if err != nil { return err } meshageTimeout = time.Duration(timeout) * time.Second // If the timeout is 0, set to "unlimited" if meshageTimeout == 0 { meshageTimeout = math.MaxInt64 } return nil } // get current value resp.Response = fmt.Sprintf("%v", meshageTimeout) return nil }
func cliLogFilter(c *minicli.Command, resp *minicli.Response) error { if len(c.StringArgs) == 0 { var filters []string loggers := log.Loggers() for _, l := range loggers { filt, _ := log.Filters(l) for _, f := range filt { var found bool for _, v := range filters { if v == f { found = true } } if !found { filters = append(filters, f) } } } if len(filters) != 0 { resp.Response = fmt.Sprintf("%v", filters) } return nil } filter := c.StringArgs["filter"] for _, l := range log.Loggers() { err := log.AddFilter(l, filter) if err != nil { return err } } return nil }
func cliLogStderr(c *minicli.Command, resp *minicli.Response) error { if c.BoolArgs["false"] { // Turn off logging to stderr log.DelLogger("stdio") } else if len(c.BoolArgs) == 0 { // Print true or false depending on whether stderr is enabled _, err := log.GetLevel("stdio") resp.Response = strconv.FormatBool(err == nil) } else if c.BoolArgs["true"] { // Enable stderr logging or adjust the level if already enabled level, _ := log.LevelInt(*f_loglevel) _, err := log.GetLevel("stdio") if err != nil { log.AddLogger("stdio", os.Stderr, level, true) } else { // TODO: Why do this? cliLogLevel updates stdio level whenever // f_loglevel is changed. log.SetLevel("stdio", level) } } return nil }
func cliDeploy(c *minicli.Command, resp *minicli.Response) error { hosts := c.StringArgs["hosts"] user := c.StringArgs["user"] sudo := c.BoolArgs["sudo"] flagsList := c.ListArgs["minimega"] if c.BoolArgs["flags"] { if flagsList == nil { resp.Response = deployGetFlags() return nil } deployFlags = flagsList return nil } hostsExpanded, err := ranges.SplitList(hosts) if err != nil { return err } log.Debug("got expanded hosts: %v", hostsExpanded) // Append timestamp to filename so that each deploy produces a new binary // on the remote system. Using the timestamp allows us to quickly identify // the latest binary after multiple deployments. fname := fmt.Sprintf("minimega_deploy_%v", time.Now().Unix()) remotePath := filepath.Join(os.TempDir(), fname) log.Debug("remotePath: %v", remotePath) // copy minimega errs := deployCopy(hostsExpanded, user, remotePath) // launch minimega on each remote node errs2 := deployRun(hostsExpanded, user, remotePath, sudo) return makeErrSlice(append(errs, errs2...)) }
func cliLogLevel(c *minicli.Command, resp *minicli.Response) error { if len(c.BoolArgs) == 0 { // Print the level resp.Response = *f_loglevel return nil } // Bool args should only have a single key that is the log level for k := range c.BoolArgs { level, err := log.LevelInt(k) if err != nil { return errors.New("unreachable") } *f_loglevel = k // forget the error, if they don't exist we shouldn't be setting // their level, so we're fine. log.SetLevel("stdio", level) log.SetLevel("file", level) log.SetLevel("syslog", level) } return nil }
func cliDisk(c *minicli.Command, resp *minicli.Response) error { image := c.StringArgs["image"] // Ensure that relative paths are always relative to /files/ if !filepath.IsAbs(image) { image = path.Join(*f_iomBase, image) } log.Debug("image: %v", image) if c.BoolArgs["snapshot"] { dst := c.StringArgs["dst"] if dst == "" { f, err := ioutil.TempFile(*f_iomBase, "snapshot") if err != nil { return errors.New("could not create a dst image") } dst = f.Name() resp.Response = dst } else if strings.Contains(dst, "/") { return errors.New("dst image must filename without path") } else { dst = path.Join(*f_iomBase, dst) } log.Debug("destination image: %v", dst) return diskSnapshot(image, dst) } else if c.BoolArgs["inject"] { partition := "1" if strings.Contains(image, ":") { parts := strings.Split(image, ":") if len(parts) != 2 { return errors.New("found way too many ':'s, expected <path/to/image>:<partition>") } image, partition = parts[0], parts[1] } options := fieldsQuoteEscape("\"", c.StringArgs["options"]) log.Debug("got options: %v", options) pairs, err := parseInjectPairs(c.ListArgs["files"]) if err != nil { return err } return diskInject(image, partition, pairs, options) } else if c.BoolArgs["create"] { size := c.StringArgs["size"] format := "raw" if _, ok := c.BoolArgs["qcow2"]; ok { format = "qcow2" } return diskCreate(format, image, size) } else if c.BoolArgs["info"] { info, err := diskInfo(image) if err != nil { return err } resp.Header = []string{"image", "format", "virtual size", "disk size", "backing file"} resp.Tabular = append(resp.Tabular, []string{ image, info.Format, info.VirtualSize, info.DiskSize, info.BackingFile, }) return nil } // boo, should be unreachable return errors.New("unreachable") }
// cliCaptureNetflow manages the CLI for starting and stopping captures to netflow. func cliCaptureNetflow(c *minicli.Command, resp *minicli.Response) error { if c.BoolArgs["delete"] { // Stop a capture return clearCapture("netflow", c.StringArgs["id"]) } else if c.BoolArgs["timeout"] { // Set or get the netflow timeout timeout := c.StringArgs["timeout"] val, err := strconv.Atoi(timeout) if timeout != "" { resp.Response = strconv.Itoa(captureNFTimeout) } else if err != nil { return fmt.Errorf("invalid timeout parameter: `%v`", timeout) } else { captureNFTimeout = val captureUpdateNFTimeouts() } return nil } else if c.BoolArgs["file"] { // Capture -> netflow (file) return startCaptureNetflowFile( c.StringArgs["bridge"], c.StringArgs["filename"], c.BoolArgs["ascii"], c.BoolArgs["gzip"], ) } else if c.BoolArgs["socket"] { // Capture -> netflow (socket) transport := "tcp" if c.BoolArgs["udp"] { transport = "udp" } return startCaptureNetflowSocket( c.StringArgs["bridge"], transport, c.StringArgs["hostname:port"], c.BoolArgs["ascii"], ) } // List captures resp.Header = []string{"ID", "Bridge", "Path", "Mode", "Compress"} for _, v := range captureEntries { if v.Type == "netflow" { row := []string{ strconv.Itoa(v.ID), v.Bridge, v.Path, v.Mode, strconv.FormatBool(v.Compress), } resp.Tabular = append(resp.Tabular, row) } } return nil // TODO: netflow stats? }
// responses func cliCCResponses(c *minicli.Command, resp *minicli.Response) error { raw := c.BoolArgs["raw"] id := c.StringArgs["id"] walker := func(path string, info os.FileInfo, err error) error { if err != nil { return err } // Test if the file looks like a UUID. If it does, and a namespace is // active, check whether the VM is part of the active namespace. This // is a fairly naive way to filter the responses... if namespace != "" && isUUID(info.Name()) { if vm := vms.FindVM(info.Name()); vm == nil { log.Debug("skipping VM: %v", info.Name()) return filepath.SkipDir } } if !info.IsDir() { log.Debug("add to response files: %v", path) data, err := ioutil.ReadFile(path) if err != nil { return err } if !raw { relPath, err := filepath.Rel(filepath.Join(*f_iomBase, ron.RESPONSE_PATH), path) if err != nil { return err } resp.Response += fmt.Sprintf("%v:\n", relPath) } resp.Response += fmt.Sprintf("%v\n", string(data)) } return nil } if id == Wildcard { // all responses return filepath.Walk(filepath.Join(*f_iomBase, ron.RESPONSE_PATH), walker) } else if _, err := strconv.Atoi(id); err == nil { p := filepath.Join(*f_iomBase, ron.RESPONSE_PATH, id) if _, err := os.Stat(p); err != nil { return fmt.Errorf("no such response dir %v", p) } return filepath.Walk(p, walker) } // try a prefix. First, do we even have anything with this prefix? ids := ccPrefixIDs(id) if len(ids) == 0 { return fmt.Errorf("no such prefix %v", id) } for _, i := range ids { p := filepath.Join(*f_iomBase, ron.RESPONSE_PATH, fmt.Sprintf("%v", i)) if _, err := os.Stat(p); err != nil { return fmt.Errorf("no such response dir %v", p) } if err := filepath.Walk(p, walker); err != nil { return err } } return nil }
func cliHistory(c *minicli.Command, resp *minicli.Response) error { resp.Response = minicli.History() return nil }
func cliVersion(c *minicli.Command, resp *minicli.Response) error { resp.Response = fmt.Sprintf("minimega %v %v", version.Revision, version.Date) return nil }
func cliRouter(c *minicli.Command, resp *minicli.Response) error { vmName := c.StringArgs["vm"] vm := vms.FindVM(vmName) if vm == nil { return vmNotFound(vmName) } if vmName != "" && len(c.BoolArgs) == 0 { // a summary of a specific router rtr := FindRouter(vm) if rtr == nil { return fmt.Errorf("vm %v not a router", vmName) } resp.Response = rtr.String() } rtr := FindOrCreateRouter(vm) if c.BoolArgs["commit"] { return rtr.Commit() } else if c.BoolArgs["log"] { var level string if c.BoolArgs["fatal"] { level = "fatal" } else if c.BoolArgs["error"] { level = "error" } else if c.BoolArgs["warn"] { level = "warn" } else if c.BoolArgs["info"] { level = "info" } else if c.BoolArgs["debug"] { level = "debug" } rtr.LogLevel(level) return nil } else if c.BoolArgs["interface"] { network, err := strconv.Atoi(c.StringArgs["network"]) if err != nil { return fmt.Errorf("invalid network: %v : %v", c.StringArgs["network"], err) } ip := c.StringArgs["IPv4/MASK"] return rtr.InterfaceAdd(network, ip) } else if c.BoolArgs["dhcp"] { addr := c.StringArgs["listen"] if c.BoolArgs["range"] { low := c.StringArgs["low"] high := c.StringArgs["high"] return rtr.DHCPAddRange(addr, low, high) } else if c.BoolArgs["router"] { r := c.StringArgs["router"] return rtr.DHCPAddRouter(addr, r) } else if c.BoolArgs["dns"] { dns := c.StringArgs["address"] return rtr.DHCPAddDNS(addr, dns) } else if c.BoolArgs["static"] { mac := c.StringArgs["mac"] ip := c.StringArgs["ip"] return rtr.DHCPAddStatic(addr, mac, ip) } } else if c.BoolArgs["dns"] { ip := c.StringArgs["ip"] hostname := c.StringArgs["hostname"] rtr.DNSAdd(ip, hostname) return nil } else if c.BoolArgs["upstream"] { ip := c.StringArgs["ip"] rtr.Upstream(ip) return nil } else if c.BoolArgs["ra"] { subnet := c.StringArgs["subnet"] rtr.RADAdd(subnet) return nil } else if c.BoolArgs["route"] { if c.BoolArgs["static"] { network := c.StringArgs["network"] nh := c.StringArgs["next-hop"] rtr.RouteStaticAdd(network, nh) return nil } else if c.BoolArgs["ospf"] { area := c.StringArgs["area"] iface := c.StringArgs["network"] rtr.RouteOSPFAdd(area, iface) } } return nil }
func cliUptime(c *minicli.Command, resp *minicli.Response) error { resp.Response = fmt.Sprintf("Started %v up %v", started, time.Since(started)) return nil }
func cliEcho(c *minicli.Command, resp *minicli.Response) error { resp.Response = strings.Join(c.ListArgs["args"], " ") return nil }
func cliVyatta(c *minicli.Command, resp *minicli.Response) error { log.Warnln("the vyatta API is deprecated and will be removed in a future release") if c.BoolArgs["dhcp"] { net := c.StringArgs["network"] if len(c.StringArgs) == 0 { // List the existing DHCP services resp.Header = []string{"Network", "GW", "Start address", "Stop address", "DNS"} resp.Tabular = [][]string{} for k, v := range vyatta.Dhcp { resp.Tabular = append(resp.Tabular, []string{k, v.Gw, v.Start, v.Stop, v.Dns}) } } else if c.StringArgs["gateway"] != "" { // Add a new DHCP service vyatta.Dhcp[net] = &vyattaDhcp{ Gw: c.StringArgs["gateway"], Start: c.StringArgs["low"], Stop: c.StringArgs["high"], Dns: c.StringArgs["dns"], } log.Debug("vyatta add dhcp %v", vyatta.Dhcp[net]) } else { // Deleting a DHCP service if _, ok := vyatta.Dhcp[net]; !ok { resp.Error = "no such Dhcp service" } else { log.Debug("vyatta delete dhcp %v", net) delete(vyatta.Dhcp, net) } } } else if c.BoolArgs["interfaces"] { // Get or update IPv4 interfaces if len(c.ListArgs) == 0 { resp.Response = fmt.Sprintf("%v", vyatta.Ipv4) } else { vyatta.Ipv4 = c.ListArgs["net"] } } else if c.BoolArgs["interfaces6"] { // Get or update IPv6 interfaces if len(c.ListArgs) == 0 { resp.Response = fmt.Sprintf("%v", vyatta.Ipv6) } else { vyatta.Ipv6 = c.ListArgs["net"] } } else if c.BoolArgs["rad"] { // Get or update rad if len(c.ListArgs) == 0 { resp.Response = fmt.Sprintf("%v", vyatta.Rad) } else { vyatta.Rad = c.ListArgs["prefix"] } } else if c.BoolArgs["ospf"] { // Get or update ospf if len(c.ListArgs) == 0 { resp.Response = fmt.Sprintf("%v", vyatta.Ospf) } else { vyatta.Ospf = c.ListArgs["network"] } } else if c.BoolArgs["ospf3"] { // Get or update ospf if len(c.ListArgs) == 0 { resp.Response = fmt.Sprintf("%v", vyatta.Ospf3) } else { vyatta.Ospf3 = c.ListArgs["network"] } } else if c.BoolArgs["routes"] { if len(c.ListArgs) == 0 { resp.Header = []string{"Network", "Route"} resp.Tabular = [][]string{} for _, v := range vyatta.Routes { resp.Tabular = append(resp.Tabular, []string{v.Route, v.NextHop}) } } else { err := vyattaUpdateRoutes(c.ListArgs["network"]) if err != nil { resp.Error = err.Error() } } } else if c.BoolArgs["config"] { // override everything and just cram the listed file into the floppy // image if len(c.StringArgs) == 0 { resp.Response = vyatta.ConfigFile } else { vyatta.ConfigFile = c.StringArgs["filename"] } } else if c.BoolArgs["write"] { var err error resp.Response, err = vyattaWrite(c.StringArgs["filename"]) if err != nil { resp.Error = err.Error() } } else { // Display info about running services var dhcpKeys []string for k, _ := range vyatta.Dhcp { dhcpKeys = append(dhcpKeys, k) } var routes []string for _, k := range vyatta.Routes { routes = append(routes, k.Route) } resp.Header = []string{ "IPv4 addresses", "IPv6 addresses", "RAD", "DHCP servers", "OSPF", "OSPF3", "Routes", } resp.Tabular = [][]string{[]string{ fmt.Sprintf("%v", vyatta.Ipv4), fmt.Sprintf("%v", vyatta.Ipv6), fmt.Sprintf("%v", vyatta.Rad), fmt.Sprintf("%v", dhcpKeys), fmt.Sprintf("%v", vyatta.Ospf), fmt.Sprintf("%v", vyatta.Ospf3), fmt.Sprintf("%v", routes), }} } return nil }
func cliOptimize(c *minicli.Command, resp *minicli.Response) error { if c.BoolArgs["ksm"] { if len(c.BoolArgs) == 1 { // Must want to print ksm status resp.Response = fmt.Sprintf("%v", ksmEnabled) } else if c.BoolArgs["true"] { // Must want to update ksm status to true ksmEnable() } else { // Must want to update ksm status to false ksmDisable() } return nil } else if c.BoolArgs["hugepages"] { if len(c.BoolArgs) == 1 { // Must want to print hugepage path resp.Response = fmt.Sprintf("%v", hugepagesMountPath) } else { hugepagesMountPath = c.StringArgs["path"] } return nil } else if c.BoolArgs["affinity"] { if len(c.BoolArgs) == 1 { // Must want to print affinity status resp.Header = []string{"CPU", "VMs"} resp.Tabular = [][]string{} var cpus []string for k, _ := range affinityCPUSets { cpus = append(cpus, k) } sort.Strings(cpus) for _, cpu := range cpus { var ids []int for _, vm := range affinityCPUSets[cpu] { ids = append(ids, vm.GetID()) } resp.Tabular = append(resp.Tabular, []string{ cpu, fmt.Sprintf("%v", ids)}) } } else if c.BoolArgs["filter"] { r, err := ranges.NewRange("", 0, runtime.NumCPU()-1) if err != nil { return fmt.Errorf("cpu affinity ranges: %v", err) } cpus, err := r.SplitRange(c.StringArgs["filter"]) if err != nil { return fmt.Errorf("cannot expand CPU range: %v", err) } affinityCPUSets = make(map[string][]*KvmVM) for _, v := range cpus { affinityCPUSets[v] = []*KvmVM{} } if affinityEnabled { affinityEnable() } } else if c.BoolArgs["true"] && !affinityEnabled { // Enabling affinity affinityEnable() } else if c.BoolArgs["false"] && affinityEnabled { // Disabling affinity affinityDisable() } return nil } // Summary of optimizations out, err := optimizeStatus() if err == nil { resp.Response = out } return err }