Beispiel #1
0
// Wrapper for minicli.ProcessCommand for commands that use meshage.
// Specifically, for `mesh send all ...`, runs the subcommand locally and
// across meshage, combining the results from the two channels into a single
// channel. This is useful if you want to get the output of a command from all
// nodes in the cluster without having to run a command locally and over
// meshage.
func runCommandGlobally(cmd *minicli.Command) chan minicli.Responses {
	// Keep the original CLI input
	original := cmd.Original
	record := cmd.Record

	cmd, err := minicli.Compilef("mesh send %s .record %t %s", Wildcard, record, original)
	if err != nil {
		log.Fatal("cannot run `%v` globally -- %v", original, err)
	}
	cmd.Record = record

	cmdLock.Lock()

	var wg sync.WaitGroup

	out := make(chan minicli.Responses)

	cmd, err = cliPreprocessor(cmd)
	if err != nil {
		log.Errorln(err)
		out <- minicli.Responses{
			&minicli.Response{
				Host:  hostname,
				Error: err.Error(),
			},
		}
		close(out)
		return out
	}

	// Run the command (should be `mesh send all ...` and the subcommand which
	// should run locally).
	ins := []chan minicli.Responses{
		minicli.ProcessCommand(cmd),
		minicli.ProcessCommand(cmd.Subcommand),
	}

	// De-mux ins into out
	for _, in := range ins {
		wg.Add(1)
		go func(in chan minicli.Responses) {
			defer wg.Done()
			for v := range in {
				out <- v
			}
		}(in)
	}

	// Wait until everything has been read before closing the chan and
	// releasing the lock.
	go func() {
		defer cmdLock.Unlock()
		defer close(out)

		wg.Wait()
	}()

	return out
}
Beispiel #2
0
// runCommands is RunCommands without locking cmdLock.
func runCommands(cmd ...*minicli.Command) <-chan minicli.Responses {
	out := make(chan minicli.Responses)

	// Preprocess all the commands so that if there's an error, we haven't
	// already started to run some of the commands.
	for i := range cmd {
		if err := cliPreprocessor(cmd[i]); err != nil {
			log.Errorln(err)

			// Send the error from a separate goroutine because nothing will
			// receive from this channel until we return. Otherwise, we will
			// cause a deadlock.
			go func() {
				out <- errResp(err)
				close(out)
			}()
			return out
		}
	}

	// Completed preprocessing run commands serially and forward all the
	// responses to out
	go func() {
		defer close(out)

		for _, c := range cmd {
			forward(minicli.ProcessCommand(c), out)
		}
	}()

	return out
}
Beispiel #3
0
// Wrapper for minicli.ProcessCommand. Ensures that the command execution lock
// is acquired before running the command.
func runCommand(cmd *minicli.Command) chan minicli.Responses {
	cmdLock.Lock()

	// Forward the responses and unlock when all are passed through
	localChan := make(chan minicli.Responses)
	go func() {
		defer cmdLock.Unlock()

		cmd, err := cliPreprocessor(cmd)
		if err != nil {
			log.Errorln(err)
			localChan <- minicli.Responses{
				&minicli.Response{
					Host:  hostname,
					Error: err.Error(),
				},
			}
			close(localChan)
			return
		}

		for resp := range minicli.ProcessCommand(cmd) {
			localChan <- resp
		}

		close(localChan)
	}()

	return localChan
}
Beispiel #4
0
func cliRead(c *minicli.Command, respChan chan minicli.Responses) {
	resp := &minicli.Response{Host: hostname}

	file, err := os.Open(c.StringArgs["file"])
	if err != nil {
		resp.Error = err.Error()
		respChan <- minicli.Responses{resp}
		return
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)

	for scanner.Scan() {
		var cmd *minicli.Command

		command := scanner.Text()
		log.Debug("read command: %v", command) // commands don't have their newlines removed

		cmd, err = minicli.CompileCommand(command)
		if err != nil {
			break
		}

		// No command was returned, must have been a blank line or a comment
		// line. Either way, don't try to run a nil command.
		if cmd == nil {
			continue
		}

		// HAX: Make sure we don't have a recursive read command
		if hasCommand(cmd, "read") {
			err = errors.New("cannot run nested `read` commands")
			break
		}

		for resp := range minicli.ProcessCommand(cmd, true) {
			respChan <- resp

			// Stop processing at the first error if there is one response.
			// TODO: What should we do if the command was mesh send and there
			// is a mixture of success and failure?
			if len(resp) == 1 && resp[0].Error != "" {
				break
			}
		}
	}

	if err != nil {
		resp.Error = err.Error()
		respChan <- minicli.Responses{resp}
	}

	if err := scanner.Err(); err != nil {
		resp.Error = err.Error()
		respChan <- minicli.Responses{resp}
	}
}
Beispiel #5
0
func main() {
	flag.Parse()

	logSetup()

	// register CLI handlers
	for i := range cliHandlers {
		err := minicli.Register(&cliHandlers[i])
		if err != nil {
			log.Fatal("invalid handler, `%v` -- %v", cliHandlers[i].HelpShort, err)
		}
	}

	var err error
	hostname, err = os.Hostname()
	if err != nil {
		log.Fatal("unable to get hostname: %v", hostname)
	}

	rond, err = ron.NewServer(*f_port, *f_path)
	if err != nil {
		log.Fatal("unable to create server: %v", err)
	}

	for {
		line, err := goreadline.Readline("rond$ ", true)
		if err != nil {
			return
		}
		command := string(line)
		log.Debug("got from stdin: `%s`", line)

		cmd, err := minicli.Compile(command)
		if err != nil {
			log.Error("%v", err)
			continue
		}

		// No command was returned, must have been a blank line or a comment
		// line. Either way, don't try to run a nil command.
		if cmd == nil {
			continue
		}

		for resp := range minicli.ProcessCommand(cmd) {
			minipager.DefaultPager.Page(resp.String())

			errs := resp.Error()
			if errs != "" {
				fmt.Fprintln(os.Stderr, errs)
			}
		}
	}
}
Beispiel #6
0
// Wrapper for minicli.ProcessCommand for commands that use meshage.
// Specifically, for `mesh send all ...`, runs the subcommand locally and
// across meshage, combining the results from the two channels into a single
// channel. This is useful if you want to get the output of a command from all
// nodes in the cluster without having to run a command locally and over
// meshage.
func runCommandGlobally(cmd *minicli.Command, record bool) chan minicli.Responses {
	cmdStr := fmt.Sprintf("mesh send %s %s", Wildcard, cmd.Original)
	cmd, err := minicli.CompileCommand(cmdStr)
	if err != nil {
		log.Fatal("cannot run `%v` globally -- %v", cmd.Original, err)
	}

	cmdLock.Lock()
	defer cmdLock.Unlock()

	var wg sync.WaitGroup

	out := make(chan minicli.Responses)

	// Run the command (should be `mesh send all ...` and the subcommand which
	// should run locally).
	ins := []chan minicli.Responses{
		minicli.ProcessCommand(cmd, record),
		minicli.ProcessCommand(cmd.Subcommand, record),
	}

	// De-mux ins into out
	for _, in := range ins {
		wg.Add(1)
		go func(in chan minicli.Responses) {
			defer wg.Done()
			for v := range in {
				out <- v
			}
		}(in)
	}

	// Wait until everything has been read before closing out
	go func() {
		wg.Wait()
		close(out)
	}()

	return out
}
Beispiel #7
0
// Wrapper for minicli.ProcessCommand. Ensures that the command execution lock
// is acquired before running the command.
func runCommand(cmd *minicli.Command) chan minicli.Responses {
	cmdLock.Lock()

	// Forward the responses and unlock when all are passed through
	localChan := make(chan minicli.Responses)
	go func() {
		defer cmdLock.Unlock()

		for resp := range minicli.ProcessCommand(cmd) {
			localChan <- resp
		}

		close(localChan)
	}()

	return localChan
}
Beispiel #8
0
// Wrapper for minicli.ProcessCommand. Ensures that the command execution lock
// is acquired before running the command.
func runCommand(cmd *minicli.Command, record bool) chan minicli.Responses {
	cmdLock.Lock()
	defer cmdLock.Unlock()

	return minicli.ProcessCommand(cmd, record)
}
Beispiel #9
0
// dot returns a graphviz 'dotfile' string representing the experiment topology
// from the perspective of this node.
func cliDot(c *minicli.Command) *minicli.Response {
	resp := &minicli.Response{Host: hostname}

	// Create the file before running any commands incase there is an error
	fout, err := os.Create(c.StringArgs["filename"])
	if err != nil {
		resp.Error = err.Error()
		return resp
	}
	defer fout.Close()

	// TODO: Rewrite to use runCommandGlobally
	cmd := minicli.MustCompile(".columns host,name,id,ip,ip6,state,vlan vm info")

	writer := bufio.NewWriter(fout)

	fmt.Fprintln(writer, "graph minimega {")
	fmt.Fprintln(writer, `size=\"8,11\";`)
	fmt.Fprintln(writer, "overlap=false;")
	//fmt.Fprintf(fout, "Legend [shape=box, shape=plaintext, label=\"total=%d\"];\n", len(n.effectiveNetwork))

	var expVms []*dotVM

	// Get info from local hosts by invoking command directly
	for resp := range minicli.ProcessCommand(cmd, false) {
		expVms = append(expVms, dotProcessInfo(resp)...)
	}

	// Get info from remote hosts over meshage
	remoteRespChan := make(chan minicli.Responses)
	go func() {
		meshageBroadcast(cmd, remoteRespChan)
		close(remoteRespChan)
	}()

	for resp := range remoteRespChan {
		expVms = append(expVms, dotProcessInfo(resp)...)
	}

	vlans := make(map[string]bool)

	for _, v := range expVms {
		color := stateToColor[v.State]
		fmt.Fprintf(writer, "%s [style=filled, color=%s];\n", v.Text, color)

		for _, c := range v.Vlans {
			fmt.Fprintf(writer, "%s -- %s\n", v.Text, c)
			vlans[c] = true
		}
	}

	for k, _ := range vlans {
		fmt.Fprintf(writer, "%s;\n", k)
	}

	fmt.Fprint(writer, "}")
	err = writer.Flush()
	if err != nil {
		resp.Error = err.Error()
	}

	return resp
}