예제 #1
0
파일: main.go 프로젝트: cdshann/minimega
func main() {
	flag.Parse()

	logSetup()

	log.Debug("using minimega: %v", *f_minimega)

	// invoke minimega and get the doc json
	doc, err := exec.Command(*f_minimega, "-cli").Output()
	if err != nil {
		log.Fatalln(err)
	}
	log.Debug("got doc: %v", string(doc))

	// decode the JSON for our template
	if err := json.Unmarshal(doc, &handlers); err != nil {
		log.Fatalln(err)
	}

	exclude = strings.Split(*f_exclude, ",")
	values = strings.Split(*f_values, ",")

	for {
		if err := fuzz(); err != nil {
			log.Fatal("fuzz: %v", err)
		}
		if err := cleanup(); err != nil {
			log.Fatal("cleanup: %v", err)
		}
	}
}
예제 #2
0
파일: mux.go 프로젝트: cdshann/minimega
func commandHandler() {
	for commands := range Client.commandChan {
		var ids []int
		for k, _ := range commands {
			ids = append(ids, k)
		}
		sort.Ints(ids)

		for _, id := range ids {
			log.Debug("ron commandHandler: %v", id)
			if id <= Client.CommandCounter {
				continue
			}

			if !Client.Matches(commands[id].Filter) {
				continue
			}
			log.Debug("ron commandHandler match: %v", id)

			processCommand(commands[id])
		}
	}

	log.Info("command handler exit")
}
예제 #3
0
파일: cli.go 프로젝트: cdshann/minimega
// cliPreprocess performs expansion on a single string and returns the update
// string or an error.
func cliPreprocess(v string) (string, error) {
	if u, err := url.Parse(v); err == nil {
		switch u.Scheme {
		case "file":
			log.Debug("file preprocessor")
			return iomHelper(u.Opaque)
		case "http", "https":
			log.Debug("http/s preprocessor")

			// Check if we've already downloaded the file
			v2, err := iomHelper(u.Path)
			if err == nil {
				return v2, err
			}

			if err.Error() == "file not found" {
				log.Info("attempting to download %v", u)

				// Try to download the file, save to files
				dst := filepath.Join(*f_iomBase, u.Path)
				if err := wget(v, dst); err != nil {
					return "", err
				}

				return dst, nil
			}

			return "", err
		}
	}

	return v, nil
}
예제 #4
0
파일: misc.go 프로젝트: ITLivLab/minimega
// Return a slice of strings, split on whitespace, not unlike strings.Fields(),
// except that quoted fields are grouped.
// 	Example: a b "c d"
// 	will return: ["a", "b", "c d"]
func fieldsQuoteEscape(c string, input string) []string {
	log.Debug("fieldsQuoteEscape splitting on %v: %v", c, input)
	f := strings.Fields(input)
	var ret []string
	trace := false
	temp := ""

	for _, v := range f {
		if trace {
			if strings.Contains(v, c) {
				trace = false
				temp += " " + trimQuote(c, v)
				ret = append(ret, temp)
			} else {
				temp += " " + v
			}
		} else if strings.Contains(v, c) {
			temp = trimQuote(c, v)
			if strings.HasSuffix(v, c) {
				// special case, single word like 'foo'
				ret = append(ret, temp)
			} else {
				trace = true
			}
		} else {
			ret = append(ret, v)
		}
	}
	log.Debug("generated: %#v", ret)
	return ret
}
예제 #5
0
파일: nbd.go 프로젝트: cdshann/minimega
// ConnectImage exports a image using the NBD protocol using the qemu-nbd. If
// successful, returns the NBD device.
func ConnectImage(image string) (string, error) {
	var nbdPath string
	var err error

	for i := 0; i < maxConnectRetries; i++ {
		nbdPath, err = GetDevice()
		if err != ErrNoDeviceAvailable {
			break
		}

		log.Debug("all nbds in use, sleeping before retrying")
		time.Sleep(time.Second * 10)
	}

	if err != nil {
		return "", err
	}

	// connect it to qemu-nbd
	cmd := exec.Command(process("qemu-nbd"), "-c", nbdPath, image)
	log.Debug("connecting to nbd with cmd: %v", cmd)

	result, err := cmd.CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("unable to connect to nbd: %v", string(result))
	}

	return nbdPath, nil
}
예제 #6
0
func containerNukeWalker(path string, info os.FileInfo, err error) error {
	if err != nil {
		return nil
	}

	log.Debug("walking file: %v", path)

	switch info.Name() {
	case "tasks":
		d, err := ioutil.ReadFile(path)
		if err != nil {
			return nil
		}

		pids := strings.Fields(string(d))
		for _, pid := range pids {
			log.Debug("found pid: %v", pid)

			fmt.Println("killing process:", pid)
			processWrapper("kill", "-9", pid)
		}
	}

	return nil
}
예제 #7
0
파일: dns.go 프로젝트: cdshann/minimega
func dnsClient() {
	rand.Seed(time.Now().UnixNano())
	t := NewEventTicker(*f_mean, *f_stddev, *f_min, *f_max)
	m := new(dns.Msg)

	for {
		t.Tick()
		h, _ := randomHost()
		d := randomDomain()
		var t uint16

		if *f_dnsv4 {
			t = dns.TypeA
		} else if *f_dnsv6 {
			t = dns.TypeAAAA
		} else {
			t = randomQuestionType()
		}

		m.SetQuestion(dns.Fqdn(d), t)
		log.Debug("dns client: question=%v", m.Question)
		in, err := dns.Exchange(m, h+addr)
		if err != nil {
			log.Debug(err.Error())
		} else {
			log.Debug("dns client: answer=%v", in)
			dnsReportChan <- 1
		}
	}
}
예제 #8
0
파일: nbd.go 프로젝트: npe9/minimega
// GetDevice returns the first available NBD. If there are no devices
// available, returns ErrNoDeviceAvailable.
func GetDevice() (string, error) {
	// Get a list of all devices
	devFiles, err := ioutil.ReadDir("/dev")
	if err != nil {
		return "", err
	}

	nbdPath := ""

	// Find the first available nbd
	for _, devInfo := range devFiles {
		dev := devInfo.Name()
		// we don't want to include partitions here
		if !strings.Contains(dev, "nbd") || strings.Contains(dev, "p") {
			continue
		}

		// check whether a pid exists for the current nbd
		_, err = os.Stat(filepath.Join("/sys/block", dev, "pid"))
		if err != nil {
			log.Debug("found available nbd: " + dev)
			nbdPath = filepath.Join("/dev", dev)
			break
		} else {
			log.Debug("nbd %v could not be used", dev)
		}
	}

	if nbdPath == "" {
		return "", ErrNoDeviceAvailable
	}

	return nbdPath, nil
}
예제 #9
0
// a filename completer for goreadline that searches for the file: prefix,
// attempts to find matching files, and returns an array of candidates.
func iomCompleter(line string) []string {
	f := strings.Fields(line)
	if len(f) == 0 {
		return nil
	}
	last := f[len(f)-1]
	if strings.HasPrefix(last, IOM_HELPER_MATCH) {
		fileprefix := strings.TrimPrefix(last, IOM_HELPER_MATCH)
		matches := iom.Info(fileprefix + "*")
		log.Debug("got raw matches: %v", matches)

		// we need to clean up matches to collapse directories, unless
		// there is a directory common prefix, in which case we
		// collapse offset by the number of common directories.
		dlcp := lcp(matches)
		didx := strings.LastIndex(dlcp, string(filepath.Separator))
		drel := ""
		if didx > 0 {
			drel = dlcp[:didx]
		}
		log.Debug("dlcp: %v, drel: %v", dlcp, drel)

		if len(fileprefix) < len(drel) {
			r := IOM_HELPER_MATCH + drel + string(filepath.Separator)
			return []string{r, r + "0"} // hack to prevent readline from fastforwarding beyond the directory name
		}

		var finalMatches []string
		for _, v := range matches {
			if strings.Contains(v, "*") {
				continue
			}
			r, err := filepath.Rel(drel, v)
			if err != nil {
				log.Errorln(err)
				return nil
			}
			dir := filepath.Dir(r)
			if dir == "." {
				finalMatches = append(finalMatches, IOM_HELPER_MATCH+v)
				continue
			}

			paths := strings.Split(dir, string(filepath.Separator))
			found := false
			for _, d := range finalMatches {
				if d == paths[0]+string(filepath.Separator) {
					found = true
					break
				}
			}
			if !found {
				finalMatches = append(finalMatches, IOM_HELPER_MATCH+filepath.Join(drel, paths[0])+string(filepath.Separator))
			}
		}

		return finalMatches
	}
	return nil
}
예제 #10
0
파일: misc.go 프로젝트: cdshann/minimega
// cmdTimeout runs the command c and returns a timeout if it doesn't complete
// after time t. If a timeout occurs, cmdTimeout will kill the process.
func cmdTimeout(c *exec.Cmd, t time.Duration) error {
	log.Debug("cmdTimeout: %v", c)

	start := time.Now()
	err := c.Start()
	if err != nil {
		return fmt.Errorf("cmd start: %v", err)
	}

	done := make(chan error)
	go func() {
		done <- c.Wait()
	}()

	select {
	case <-time.After(t):
		err = c.Process.Kill()
		if err != nil {
			return err
		}
		return <-done
	case err = <-done:
		log.Debug("cmd %v completed in %v", c, time.Now().Sub(start))
		return err
	}
}
예제 #11
0
파일: vnc.go 프로젝트: cdshann/minimega
func vncClear() {
	for k, v := range vncKBRecording {
		if inNamespace(v.VM) {
			log.Debug("stopping kb recording for %v", k)
			if err := v.Stop(); err != nil {
				log.Error("%v", err)
			}

			delete(vncKBRecording, k)
		}
	}

	for k, v := range vncFBRecording {
		if inNamespace(v.VM) {
			log.Debug("stopping fb recording for %v", k)
			if err := v.Stop(); err != nil {
				log.Error("%v", err)
			}

			delete(vncFBRecording, k)
		}
	}

	for k, v := range vncPlaying {
		if inNamespace(v.VM) {
			log.Debug("stopping kb playing for %v", k)
			if err := v.Stop(); err != nil {
				log.Error("%v", err)
			}

			delete(vncPlaying, k)
		}
	}
}
예제 #12
0
// walk every arg looking for "file:" and calling iomHelper on the suffix.
// Replace the arg with the local file if found.
func iomPreprocessor(c *minicli.Command) (*minicli.Command, error) {
	for k, v := range c.StringArgs {
		if strings.HasPrefix(v, IOM_HELPER_MATCH) {
			file := strings.TrimPrefix(v, IOM_HELPER_MATCH)
			local, err := iomHelper(file)
			if err != nil {
				return nil, err
			}
			log.Debug("iomPreProcessor: %v -> %v", v, local)
			c.StringArgs[k] = local
		}
	}
	for k, v := range c.ListArgs {
		for x, y := range v {
			if strings.HasPrefix(y, IOM_HELPER_MATCH) {
				file := strings.TrimPrefix(y, IOM_HELPER_MATCH)
				local, err := iomHelper(file)
				if err != nil {
					return nil, err
				}
				log.Debug("iomPreProcessor: %v -> %v", y, local)
				c.ListArgs[k][x] = local
			}
		}
	}
	return c, nil
}
예제 #13
0
func deployRun(hosts []string, user, remotePath string, sudo bool) []error {
	log.Debug("deployRun: %v, %v", hosts, user)

	var errs []error

	// minimega command
	flags := deployGetFlags()
	log.Debug("minimega flags: %v", flags)

	var minimegaCommand string
	if sudo {
		minimegaCommand = fmt.Sprintf("sudo -b nohup %v %v > /dev/null 2>&1 &", remotePath, flags)
	} else {
		minimegaCommand = fmt.Sprintf("nohup %v %v > /dev/null 2>&1 &", remotePath, flags)
	}

	for _, host := range hosts {
		command := []string{"ssh", "-o", "StrictHostKeyChecking=no"}
		if user != "" {
			command = append(command, fmt.Sprintf("%v@%v", user, host))
		} else {
			command = append(command, fmt.Sprintf("%v", host))
		}
		command = append(command, minimegaCommand)
		log.Debug("ssh command: %v", command)

		out, err := processWrapper(command...)
		if err != nil {
			errs = append(errs, fmt.Errorf("%v: %v", err, out))
		}
	}

	return errs
}
예제 #14
0
파일: tunnel.go 프로젝트: cdshann/minimega
// Trunk reads data from remote, constructs a *Message, and sends it using fn.
// Returns the first error.
func Trunk(remote net.Conn, uuid string, fn func(*Message) error) {
	var n int
	var err error

	for err == nil {
		buf := make([]byte, 32*1024)
		n, err = remote.Read(buf)
		log.Debug("trunking %v bytes for %v", n, uuid)
		if err == nil {
			m := &Message{
				Type:   MESSAGE_TUNNEL,
				UUID:   uuid,
				Tunnel: buf[:n],
			}

			err = fn(m)
		}

		if err != nil && err != io.ErrClosedPipe {
			log.Errorln(err)
		}
	}

	if err != io.ErrClosedPipe {
		log.Error("trunk failed for %v: %v", err)
	}

	log.Debug("trunk exit for %v", uuid)
}
예제 #15
0
파일: node.go 프로젝트: summits/minimega
func (n *Node) handleMSA(m *Message) {
	log.Debug("handleMSA: %v", m)

	n.meshLock.Lock()
	defer n.meshLock.Unlock()

	if len(n.network[m.Source]) == len(m.Body.([]string)) {
		diff := false
		for i, v := range n.network[m.Source] {
			if m.Body.([]string)[i] != v {
				diff = true
				break
			}
		}
		if !diff {
			log.Debugln("MSA discarded, client data hasn't changed")
			return
		}
	}

	n.network[m.Source] = m.Body.([]string)

	if log.WillLog(log.DEBUG) {
		log.Debug("new network is: %v", n.network)
	}

	n.updateNetwork = true
}
예제 #16
0
func (vm *ContainerVM) console(stdin, stdout, stderr *os.File) {
	socketPath := filepath.Join(vm.instancePath, "console")
	l, err := net.Listen("unix", socketPath)
	if err != nil {
		log.Error("could not start unix domain socket console on vm %v: %v", vm.ID, err)
		return
	}
	vm.listener = l

	for {
		conn, err := l.Accept()
		if err != nil {
			if strings.Contains(err.Error(), "use of closed network connection") {
				return
			}
			log.Error("console socket on vm %v: %v", vm.ID, err)
			continue
		}
		log.Debug("new connection!")

		go io.Copy(conn, stdout)
		go io.Copy(conn, stderr)
		io.Copy(stdin, conn)
		log.Debug("disconnected!")
	}
}
예제 #17
0
func unmangleUUID(uuid string) string {
	// string must be in the form:
	//	XXXXXXXX-XXXX-XXXX-YYYY-YYYYYYYYYYYY
	// the X characters are reversed at 2 byte intervals (big/little endian for a uuid?)
	var ret string
	re := regexp.MustCompile("[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}")

	u := re.FindString(strings.ToLower(uuid))
	if uuid == "" {
		log.Fatal("uuid failed to match uuid format: %v", uuid)
	}

	log.Debug("found uuid: %v", u)

	if getOSVer() != "Windows XP" {
		return u
	}

	ret += u[6:8]
	ret += u[4:6]
	ret += u[2:4]
	ret += u[:2]
	ret += "-"
	ret += u[11:13]
	ret += u[9:11]
	ret += "-"
	ret += u[16:18]
	ret += u[14:16]
	ret += u[18:]

	log.Debug("mangled/unmangled uuid: %v %v", u, ret)
	return ret
}
예제 #18
0
파일: client.go 프로젝트: cdshann/minimega
// Matches tests whether all the filters match the client.
func (c *Client) Matches(f *Filter) bool {
	if f == nil {
		return true
	}

	if f.UUID != "" && f.UUID != c.UUID {
		log.Debug("failed match on UUID: %v != %v", f.UUID, c.UUID)
		return false
	}
	if f.Hostname != "" && f.Hostname != c.Hostname {
		log.Debug("failed match on hostname: %v != %v", f.Hostname, c.Hostname)
		return false
	}
	if f.Arch != "" && f.Arch != c.Arch {
		log.Debug("failed match on arch: %v != %v", f.Arch, c.Arch)
		return false
	}
	if f.OS != "" && f.OS != c.OS {
		log.Debug("failed match on os: %v != %v", f.OS, c.OS)
		return false
	}

	for k, v := range f.Tags {
		if c.Tags[k] != v {
			log.Debug("failed match on tag %v, %v != %v", k, v, c.Tags[k])
			return false
		}
	}

	return c.matchesIP(f) && c.matchesMAC(f)
}
예제 #19
0
파일: mega.go 프로젝트: summits/minimega
func parseMega(ctx *present.Context, sourceFile string, sourceLine int, cmd string) (present.Elem, error) {
	cmd = strings.TrimSpace(cmd)
	log.Debug("parseMega cmd: %v", cmd)

	f := strings.Fields(cmd)
	if len(f) != 2 {
		return nil, fmt.Errorf("invalid .mega directive: %v", cmd)
	}

	filename := filepath.Join(*f_root, filepath.Dir(sourceFile), f[1])
	log.Debug("filename: %v", filename)

	text, err := ctx.ReadFile(filename)
	if err != nil {
		return nil, fmt.Errorf("%s:%d: %v", sourceFile, sourceLine, err)
	}

	data := &megaTemplateData{
		Body: string(text),
	}

	var buf bytes.Buffer
	if err := megaTemplate.Execute(&buf, data); err != nil {
		return nil, err
	}

	return Mega{
		Text:     template.HTML(buf.String()),
		Filename: filepath.Base(filename),
	}, nil
}
예제 #20
0
파일: socket.go 프로젝트: npe9/minimega
func (m *megaconn) start() {
	log.Debug("got body: %v", m.body)
	lines := strings.Split(m.body, "\n")
	for _, v := range lines {
		resp, errs := sendCommand(v)
		mess := &Message{
			Id: m.id,
		}
		if errs != "" {
			mess.Kind = "stderr"
			mess.Body = errs
		} else {
			mess.Kind = "stdout"
			mess.Body = resp
		}
		log.Debug("generated message: %v", mess)
		m.out <- mess
	}

	mess := &Message{
		Id:   m.id,
		Kind: "end",
	}
	time.AfterFunc(msgDelay, func() { m.out <- mess })
}
예제 #21
0
func deployCopy(hosts []string, user, remotePath string) []error {
	log.Debug("deployCopy: %v, %v", hosts, user)

	var errs []error

	minimegaBinary := fmt.Sprintf("/proc/%v/exe", os.Getpid())
	log.Debug("minimega binary: %v", minimegaBinary)

	for _, host := range hosts {
		command := []string{"scp", "-B", "-o", "StrictHostKeyChecking=no", minimegaBinary}
		if user != "" {
			command = append(command, fmt.Sprintf("%v@%v:%v", user, host, remotePath))
		} else {
			command = append(command, fmt.Sprintf("%v:%v", host, remotePath))
		}
		log.Debug("scp command: %v", command)

		out, err := processWrapper(command...)
		if err != nil {
			errs = append(errs, fmt.Errorf("%v: %v", err, out))
		}
	}

	return errs
}
예제 #22
0
파일: process.go 프로젝트: cdshann/minimega
// cmdTimeout runs the command c and returns a timeout if it doesn't complete
// after time t. If a timeout occurs, cmdTimeout will kill the process. Blocks
// until the process terminates.
func cmdTimeout(c *exec.Cmd, t time.Duration) error {
	log.Debug("cmdTimeout: %v", c)

	start := time.Now()
	if err := c.Start(); err != nil {
		return fmt.Errorf("cmd start: %v", err)
	}

	done := make(chan error)
	go func() {
		done <- c.Wait()
		close(done)
	}()

	select {
	case <-time.After(t):
		log.Debug("killing cmd %v", c)
		err := c.Process.Kill()
		// Receive from done so that we don't leave the goroutine hanging
		err2 := <-done
		// Kill error takes precedence as they should be unexpected
		if err != nil {
			return err
		}
		return err2
	case err := <-done:
		log.Debug("cmd %v completed in %v", c, time.Now().Sub(start))
		return err
	}
}
예제 #23
0
파일: server.go 프로젝트: cdshann/minimega
// Dial a client serial port. The server will maintain this connection until a
// client connects and then disconnects.
func (s *Server) DialSerial(path string) error {
	log.Debug("DialSerial: %v", path)

	s.serialLock.Lock()
	defer s.serialLock.Unlock()

	// are we already connected to this client?
	if _, ok := s.serialConns[path]; ok {
		return fmt.Errorf("already connected to serial client %v", path)
	}

	// connect!
	serial, err := net.Dial("unix", path)
	if err != nil {
		return err
	}

	s.serialConns[path] = serial
	go func() {
		s.clientHandler(serial)

		// client disconnected
		s.serialLock.Lock()
		defer s.serialLock.Unlock()

		delete(s.serialConns, path)
		log.Debug("disconnected from serial client: %v", path)
	}()

	return nil
}
예제 #24
0
파일: server.go 프로젝트: cdshann/minimega
// Starts a Ron server on the specified port
func (s *Server) Listen(port int) error {
	ln, err := net.Listen("tcp", fmt.Sprintf(":%v", port))
	if err != nil {
		return err
	}

	go func() {
		for {
			conn, err := ln.Accept()
			if err != nil {
				log.Errorln(err)
				return
			}

			log.Debug("new connection from: %v", conn.RemoteAddr())

			go func() {
				addr := conn.RemoteAddr()
				s.clientHandler(conn)
				log.Debug("disconnected from: %v", addr)
			}()
		}
	}()

	return nil
}
예제 #25
0
파일: vnc.go 프로젝트: npe9/minimega
func vncClear() error {
	for k, v := range vncKBRecording {
		log.Debug("stopping kb recording for %v", k)
		if err := v.Stop(); err != nil {
			log.Error("%v", err)
		}

		delete(vncKBRecording, k)
	}

	for k, v := range vncFBRecording {
		log.Debug("stopping fb recording for %v", k)
		if err := v.Stop(); err != nil {
			log.Error("%v", err)
		}

		delete(vncFBRecording, k)
	}

	for k, v := range vncKBPlaying {
		log.Debug("stopping kb playing for %v", k)
		if err := v.Stop(); err != nil {
			log.Error("%v", err)
		}

		delete(vncKBPlaying, k)
	}

	return nil
}
예제 #26
0
파일: nuke.go 프로젝트: cdshann/minimega
// Walks the f_base directory and kills procs read from any qemu or
// dnsmasq pid files
func nukeWalker(path string, info os.FileInfo, err error) error {
	if err != nil {
		return nil
	}

	log.Debug("walking file: %v", path)

	switch info.Name() {
	case "qemu.pid", "dnsmasq.pid":
		d, err := ioutil.ReadFile(path)
		t := strings.TrimSpace(string(d))
		log.Debug("found pid: %v", t)
		if err != nil {
			return err
		}

		args := []string{
			"kill",
			t,
		}
		log.Infoln("killing process:", t)

		out, err := processWrapper(args...)
		if err != nil {
			log.Error("%v: %v", err, out)
		}
	}
	return nil
}
예제 #27
0
파일: apigen.go 프로젝트: cdshann/minimega
func main() {
	flag.Parse()

	logSetup()

	log.Debug("using minimega: %v", *f_minimega)
	log.Debug("using doc template: %v", *f_template)

	// invoke minimega and get the doc json
	doc, err := exec.Command(*f_minimega, "-cli").Output()
	if err != nil {
		log.Fatalln(err)
	}
	log.Debug("got doc: %v", string(doc))

	// decode the JSON for our template
	var handlers []*minicli.Handler
	err = json.Unmarshal(doc, &handlers)
	if err != nil {
		log.Fatalln(err)
	}

	// populate the apigen date for the template
	year, month, day := time.Now().Date()
	api := apigen{
		Date: fmt.Sprintf("%v %v %v", day, month, year),
	}

	// populate the major sections for the template
	for _, v := range handlers {
		var p string
		if strings.HasPrefix(v.SharedPrefix, "clear") {
			p = strings.TrimPrefix(v.SharedPrefix, "clear ")
		} else {
			p = v.SharedPrefix
		}
		if strings.HasPrefix(p, ".") {
			api.Builtins = append(api.Builtins, v)
		} else if strings.HasPrefix(p, "mesh") {
			api.Mesh = append(api.Mesh, v)
		} else if strings.HasPrefix(p, "vm") {
			api.VM = append(api.VM, v)
		} else {
			api.Host = append(api.Host, v)
		}
	}

	// run the template and print to stdout
	var out bytes.Buffer
	t, err := template.ParseFiles(*f_template)
	if err != nil {
		log.Fatalln(err)
	}
	err = t.Execute(&out, api)
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Println(out.String())
}
예제 #28
0
파일: route.go 프로젝트: cdshann/minimega
// find and record the next hop route for c.
// Additionally, all hops along this route are also the shortest path, so
// record those as well to save on effort.
func (n *Node) updateRoute(c string) {
	if len(n.effectiveNetwork) == 0 {
		return
	}

	if log.WillLog(log.DEBUG) {
		log.Debug("updating route for %v", c)
	}

	routes := make(map[string]string) // a key node has a value of the previous hop, the key exists if it's been visited
	routes[n.name] = n.name           // the route to ourself is pretty easy to calculate

	// dijkstra's algorithm is well suited in go - we can use a buffered
	// channel of nodes to order our search. We start by putting ourselves
	// in the queue (channel) and follow all nodes connected to it. If we
	// haven't visited that node before, it goes in the queue.
	q := make(chan string, len(n.effectiveNetwork))
	q <- n.name

	for len(q) != 0 {
		v := <-q

		if log.WillLog(log.DEBUG) {
			log.Debug("visiting %v", v)
		}

		for _, a := range n.effectiveNetwork[v] {
			if _, ok := routes[a]; !ok {
				q <- a
				if log.WillLog(log.DEBUG) {
					log.Debug("previous hop for %v is %v", a, v)
				}
				routes[a] = v // this is the route to node a from v
			}
		}

		if v == c {
			break
		}
	}

	for k, v := range routes {
		curr := v
		prev := k
		r := k
		if curr == n.name {
			r += "<-" + routes[curr]
		}
		for curr != n.name {
			prev = curr
			curr = routes[curr]
			r += "<-" + routes[curr]
		}
		if log.WillLog(log.DEBUG) {
			log.Debug("full route for %v is %v", k, r)
		}
		n.routes[k] = prev
	}
}
예제 #29
0
파일: node.go 프로젝트: summits/minimega
// newConnection processes a new incoming connection from another node, processes the connection
// handshake, adds the connection to the client list, and starts the client message handler.
func (n *Node) newConnection(conn net.Conn) {
	log.Debug("newConnection: %v", conn.RemoteAddr().String())

	// are we soliciting connections?
	var solicited bool
	if uint(len(n.clients)) < n.degree {
		solicited = true
	} else {
		solicited = false
	}
	log.Debug("solicited: %v", solicited)

	c := &client{
		conn: conn,
		enc:  gob.NewEncoder(conn),
		dec:  gob.NewDecoder(conn),
		ack:  make(chan uint64, RECEIVE_BUFFER),
	}

	// the handshake involves the following:
	// 1.  We send our name and our solicitation status
	// 2a. If the connection is solicited but we're all full, the remote node simply hangs up
	// 2b. If the connection is unsolicited or solicited and we are still soliciting connections, the remote node responds with its name
	// 3.  The connection is valid, add it to our client list and broadcast a MSA announcing the new connection.
	// 4.  The remote node does the same as 3.
	err := c.enc.Encode(n.name)
	if err != nil {
		log.Error("newConnection encode name: %v: %v", n.name, err)
		c.conn.Close()
		return
	}

	err = c.enc.Encode(solicited)
	if err != nil {
		log.Error("newConnection encode solicited: %v: %v", n.name, err)
		c.conn.Close()
		return
	}

	var resp string
	err = c.dec.Decode(&resp)
	if err != nil {
		if err != io.EOF {
			log.Error("newConnection decode name: %v: %v", n.name, err)
		}
		c.conn.Close()
		return
	}

	c.name = resp
	log.Debug("handshake from: %v", c.name)

	n.clientLock.Lock()
	n.clients[resp] = c
	n.clientLock.Unlock()

	go n.clientHandler(resp)
}
예제 #30
0
파일: message.go 프로젝트: cdshann/minimega
// Send a message according to the parameters set in the message.
// Users will generally use the Set and Broadcast functions instead of Send.
// The returned error is always nil if the message type is broadcast.
// If an error is encountered, Send returns immediately.
func (n *Node) Send(m *Message) ([]string, error) {
	if log.WillLog(log.DEBUG) {
		log.Debug("Send: %v", m)
	}

	// force updating the network if needed on Send()
	n.checkUpdateNetwork()

	routeSlices, err := n.getRoutes(m)
	if err != nil {
		return nil, err
	}

	if log.WillLog(log.DEBUG) {
		log.Debug("routeSlices: %v", routeSlices)
	}

	errChan := make(chan error)
	for k, v := range routeSlices {
		go func(client string, recipients []string) {
			mOne := &Message{
				Recipients:   recipients,
				Source:       m.Source,
				CurrentRoute: m.CurrentRoute,
				Command:      m.Command,
				Body:         m.Body,
			}
			err := n.clientSend(client, mOne)
			if err != nil {
				errChan <- err
			} else {
				errChan <- nil
			}
		}(k, v)
	}

	// wait on all of the client sends to complete
	var ret string
	for i := 0; i < len(routeSlices); i++ {
		r := <-errChan
		if r != nil {
			ret += r.Error() + "\n"
		}
	}

	// Rebuild the recipients from the routeSlices so that the caller can know
	// which recipients were actually valid.
	recipients := []string{}
	for _, r := range routeSlices {
		recipients = append(recipients, r...)
	}

	if ret == "" {
		return recipients, nil
	}

	return recipients, errors.New(ret)
}